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,5 +1,5 @@
|
|
|
1
|
-
import { Type } from "@sinclair/typebox";
|
|
2
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Type } from "@sinclair/typebox";
|
|
3
3
|
import { promptUser } from "../../onboarding/prompt-user.js";
|
|
4
4
|
import type { AskUserHandler } from "../../types/index.js";
|
|
5
5
|
|
|
@@ -14,14 +14,15 @@ const AskUserParams = Type.Object({
|
|
|
14
14
|
question: Type.String({ description: "The question to ask the user" }),
|
|
15
15
|
question_type: Type.Union(
|
|
16
16
|
[Type.Literal("select"), Type.Literal("text"), Type.Literal("confirm")],
|
|
17
|
-
{
|
|
17
|
+
{
|
|
18
|
+
description:
|
|
19
|
+
"Type of answer expected: select (pick from options), text (free input), or confirm (yes/no)",
|
|
20
|
+
},
|
|
18
21
|
),
|
|
19
22
|
options: Type.Optional(
|
|
20
23
|
Type.Array(Type.String(), { description: "Choices for select-type questions" }),
|
|
21
24
|
),
|
|
22
|
-
placeholder: Type.Optional(
|
|
23
|
-
Type.String({ description: "Hint text for text input" }),
|
|
24
|
-
),
|
|
25
|
+
placeholder: Type.Optional(Type.String({ description: "Hint text for text input" })),
|
|
25
26
|
reason: Type.Optional(
|
|
26
27
|
Type.String({ description: "Brief context for why this clarification is needed" }),
|
|
27
28
|
),
|
|
@@ -45,7 +46,12 @@ export function registerAskUserTool(pi: ExtensionAPI, askUserHandler?: AskUserHa
|
|
|
45
46
|
// model/caller bug, not a cancellation.
|
|
46
47
|
if (questionType === "select" && (!params.options || params.options.length === 0)) {
|
|
47
48
|
return {
|
|
48
|
-
content: [
|
|
49
|
+
content: [
|
|
50
|
+
{
|
|
51
|
+
type: "text",
|
|
52
|
+
text: "Error: No options provided for select question. Provide options or use text type instead.",
|
|
53
|
+
},
|
|
54
|
+
],
|
|
49
55
|
details: { question, questionType, answer: null, cancelled: true } as AskUserDetails,
|
|
50
56
|
};
|
|
51
57
|
}
|
|
@@ -63,9 +69,10 @@ export function registerAskUserTool(pi: ExtensionAPI, askUserHandler?: AskUserHa
|
|
|
63
69
|
);
|
|
64
70
|
|
|
65
71
|
if (result.cancelled) {
|
|
66
|
-
const text =
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
const text =
|
|
73
|
+
!askUserHandler && !ctx?.hasUI
|
|
74
|
+
? "UI not available (non-interactive mode). Proceed with your best judgment and clearly disclose your assumption."
|
|
75
|
+
: "User cancelled the selection. Proceed with your best judgment and disclose your assumption.";
|
|
69
76
|
return {
|
|
70
77
|
content: [{ type: "text", text }],
|
|
71
78
|
details: { question, questionType, answer: null, cancelled: true } as AskUserDetails,
|
|
@@ -74,7 +81,12 @@ export function registerAskUserTool(pi: ExtensionAPI, askUserHandler?: AskUserHa
|
|
|
74
81
|
|
|
75
82
|
return {
|
|
76
83
|
content: [{ type: "text", text: `User answered: ${result.answer}` }],
|
|
77
|
-
details: {
|
|
84
|
+
details: {
|
|
85
|
+
question,
|
|
86
|
+
questionType,
|
|
87
|
+
answer: result.answer,
|
|
88
|
+
cancelled: false,
|
|
89
|
+
} as AskUserDetails,
|
|
78
90
|
};
|
|
79
91
|
},
|
|
80
92
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mkdirSync } from "node:fs";
|
|
2
|
-
import type { BrowserContext } from "playwright-core";
|
|
3
|
-
import { Type } from "@sinclair/typebox";
|
|
4
2
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { Type } from "@sinclair/typebox";
|
|
4
|
+
import type { BrowserContext } from "playwright-core";
|
|
5
5
|
|
|
6
6
|
interface TwitterLoginResult {
|
|
7
7
|
success: boolean;
|
|
@@ -47,7 +47,10 @@ export async function runTwitterLogin(notify: (msg: string) => void): Promise<Tw
|
|
|
47
47
|
if (found.length >= 2) {
|
|
48
48
|
await page.waitForTimeout(3000);
|
|
49
49
|
await context.close();
|
|
50
|
-
return {
|
|
50
|
+
return {
|
|
51
|
+
success: true,
|
|
52
|
+
message: `Twitter login successful! (${found.map((c) => c.name).join(", ")})`,
|
|
53
|
+
};
|
|
51
54
|
}
|
|
52
55
|
} catch {
|
|
53
56
|
return { success: false, message: "Twitter login cancelled (browser closed)." };
|
|
@@ -58,7 +61,11 @@ export async function runTwitterLogin(notify: (msg: string) => void): Promise<Tw
|
|
|
58
61
|
await context.close();
|
|
59
62
|
return { success: false, message: "Twitter login timed out after 5 minutes." };
|
|
60
63
|
} catch (error) {
|
|
61
|
-
try {
|
|
64
|
+
try {
|
|
65
|
+
await context.close();
|
|
66
|
+
} catch {
|
|
67
|
+
/* already closed */
|
|
68
|
+
}
|
|
62
69
|
const msg = error instanceof Error ? error.message : String(error);
|
|
63
70
|
return { success: false, message: `Twitter login failed: ${msg}` };
|
|
64
71
|
}
|
|
@@ -77,7 +84,12 @@ export function registerTwitterLoginTool(pi: ExtensionAPI): void {
|
|
|
77
84
|
async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
|
|
78
85
|
if (!ctx?.hasUI) {
|
|
79
86
|
return {
|
|
80
|
-
content: [
|
|
87
|
+
content: [
|
|
88
|
+
{
|
|
89
|
+
type: "text",
|
|
90
|
+
text: "Cannot open browser in non-interactive mode. Twitter login requires a terminal session with UI.",
|
|
91
|
+
},
|
|
92
|
+
],
|
|
81
93
|
details: { success: false },
|
|
82
94
|
};
|
|
83
95
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Type } from "@sinclair/typebox";
|
|
2
1
|
import type { AgentTool } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import { Type } from "@sinclair/typebox";
|
|
3
3
|
import { getFearGreedIndex } from "../../providers/fear-greed.js";
|
|
4
4
|
import { wrapProvider } from "../../providers/wrap-provider.js";
|
|
5
5
|
import type { FearGreedData } from "../../types/sentiment.js";
|
|
@@ -12,7 +12,7 @@ export const fearGreedTool: AgentTool<typeof params, FearGreedData> = {
|
|
|
12
12
|
description:
|
|
13
13
|
"Get the Crypto Fear & Greed Index (alternative.me) — a sentiment indicator from 0 (Extreme Fear) to 100 (Extreme Greed). Includes current value and previous close.",
|
|
14
14
|
parameters: params,
|
|
15
|
-
async execute(
|
|
15
|
+
async execute(_toolCallId, _args) {
|
|
16
16
|
const result = await wrapProvider("feargreed", () => getFearGreedIndex());
|
|
17
17
|
if (result.status === "unavailable") {
|
|
18
18
|
return {
|
|
@@ -1,54 +1,92 @@
|
|
|
1
|
-
import { Type } from "@sinclair/typebox";
|
|
2
1
|
import type { AgentTool } from "@earendil-works/pi-agent-core";
|
|
3
|
-
import {
|
|
4
|
-
import { wrapProvider } from "../../providers/wrap-provider.js";
|
|
2
|
+
import { Type } from "@sinclair/typebox";
|
|
5
3
|
import { getConfig } from "../../config.js";
|
|
6
4
|
import { withCredentialCheck } from "../../onboarding/tool-helpers.js";
|
|
7
|
-
import {
|
|
5
|
+
import { getSeries } from "../../providers/fred.js";
|
|
6
|
+
import { wrapProvider } from "../../providers/wrap-provider.js";
|
|
8
7
|
import type { FredSeries } from "../../types/macro.js";
|
|
8
|
+
import { FRED_SERIES } from "../../types/macro.js";
|
|
9
9
|
|
|
10
10
|
const params = Type.Object({
|
|
11
11
|
series_id: Type.String({
|
|
12
|
-
description: `FRED series ID. Common ones: ${Object.entries(FRED_SERIES)
|
|
12
|
+
description: `FRED series ID. Common ones: ${Object.entries(FRED_SERIES)
|
|
13
|
+
.map(([k, v]) => `${v} (${k})`)
|
|
14
|
+
.join(", ")}`,
|
|
13
15
|
}),
|
|
14
16
|
limit: Type.Optional(
|
|
15
|
-
Type.
|
|
17
|
+
Type.Integer({
|
|
18
|
+
minimum: 1,
|
|
19
|
+
maximum: 1000,
|
|
20
|
+
description: "Number of observations to return. Default: 30",
|
|
21
|
+
}),
|
|
16
22
|
),
|
|
17
23
|
});
|
|
18
24
|
|
|
19
|
-
export const fredDataTool: AgentTool<typeof params, FredSeries | { credentialRequired: unknown }> =
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
: "";
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
25
|
+
export const fredDataTool: AgentTool<typeof params, FredSeries | { credentialRequired: unknown }> =
|
|
26
|
+
{
|
|
27
|
+
name: "get_economic_data",
|
|
28
|
+
label: "FRED Economic Data",
|
|
29
|
+
description:
|
|
30
|
+
"Get economic data from FRED (Federal Reserve Economic Data): interest rates, CPI, GDP, unemployment, yield curve, and more. Requires FRED.",
|
|
31
|
+
parameters: params,
|
|
32
|
+
async execute(_toolCallId, args) {
|
|
33
|
+
return withCredentialCheck("fred", async () => {
|
|
34
|
+
const apiKey = getConfig().fredApiKey!;
|
|
35
|
+
const seriesId = args.series_id.toUpperCase();
|
|
36
|
+
const limit = normalizeLimit(seriesId, args.limit);
|
|
37
|
+
const result = await wrapProvider("fred", () => getSeries(seriesId, apiKey, limit));
|
|
38
|
+
if (result.status === "unavailable") {
|
|
39
|
+
return {
|
|
40
|
+
content: [
|
|
41
|
+
{ type: "text", text: `⚠ FRED data unavailable for ${seriesId} (${result.reason}).` },
|
|
42
|
+
],
|
|
43
|
+
details: null as any,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const series = result.data;
|
|
47
|
+
|
|
48
|
+
const latest = series.observations[series.observations.length - 1];
|
|
49
|
+
const header = `**${series.title}** (${series.id})`;
|
|
50
|
+
const meta = `Units: ${series.units} | Frequency: ${series.frequency} | Last updated: ${series.lastUpdated}`;
|
|
51
|
+
const current = latest ? `Latest: ${latest.value} (${latest.date})` : "No data";
|
|
52
|
+
const derived = formatDerivedChange(series);
|
|
53
|
+
|
|
54
|
+
// Show last 10 observations
|
|
55
|
+
const recent = series.observations.slice(-10);
|
|
56
|
+
const table = recent.map((o) => `${o.date}: ${o.value}`).join("\n");
|
|
57
|
+
|
|
58
|
+
const text = [header, meta, current, derived, "", "Recent observations:", table]
|
|
59
|
+
.filter(Boolean)
|
|
60
|
+
.join("\n");
|
|
61
|
+
const prefix = result.stale
|
|
62
|
+
? `⚠ Using cached FRED data from ${result.timestamp} (FRED unavailable)\n`
|
|
63
|
+
: "";
|
|
64
|
+
return { content: [{ type: "text", text: prefix + text }], details: series };
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
function normalizeLimit(seriesId: string, requested: number | undefined): number {
|
|
70
|
+
const limit = requested ?? 30;
|
|
71
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 1000) {
|
|
72
|
+
throw new Error("limit must be an integer between 1 and 1000.");
|
|
73
|
+
}
|
|
74
|
+
if (seriesId === FRED_SERIES.CPI && limit < 13) return 13;
|
|
75
|
+
return limit;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function formatDerivedChange(series: FredSeries): string | null {
|
|
79
|
+
const latest = series.observations.at(-1);
|
|
80
|
+
if (!latest) return null;
|
|
81
|
+
if (!/monthly/i.test(series.frequency) || !/\bindex\b/i.test(series.units)) return null;
|
|
82
|
+
|
|
83
|
+
const latestYear = Number(latest.date.slice(0, 4));
|
|
84
|
+
const monthDay = latest.date.slice(4);
|
|
85
|
+
const prior = series.observations.find(
|
|
86
|
+
(observation) => observation.date === `${latestYear - 1}${monthDay}`,
|
|
87
|
+
);
|
|
88
|
+
if (!prior || prior.value === 0) return null;
|
|
89
|
+
|
|
90
|
+
const pct = (latest.value / prior.value - 1) * 100;
|
|
91
|
+
return `Derived YoY change: ${pct.toFixed(2)}% (${prior.date} to ${latest.date}).`;
|
|
92
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { Type } from "@sinclair/typebox";
|
|
2
1
|
import type { AgentTool } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import { Type } from "@sinclair/typebox";
|
|
3
3
|
import { getCryptoHistory } from "../../providers/coingecko.js";
|
|
4
4
|
import { wrapProvider } from "../../providers/wrap-provider.js";
|
|
5
5
|
import type { OHLCV } from "../../types/market.js";
|
|
6
|
+
import { computeRiskMetrics } from "../portfolio/risk-analysis.js";
|
|
6
7
|
|
|
7
8
|
const params = Type.Object({
|
|
8
9
|
id: Type.String({
|
|
@@ -20,13 +21,15 @@ export const cryptoHistoryTool: AgentTool<typeof params, OHLCV[]> = {
|
|
|
20
21
|
label: "Crypto History",
|
|
21
22
|
description: "Get historical OHLC data for a cryptocurrency",
|
|
22
23
|
parameters: params,
|
|
23
|
-
async execute(
|
|
24
|
+
async execute(_toolCallId, args) {
|
|
24
25
|
const id = args.id.toLowerCase();
|
|
25
26
|
const days = args.days ?? 180;
|
|
26
27
|
const result = await wrapProvider("coingecko", () => getCryptoHistory(id, days));
|
|
27
28
|
if (result.status === "unavailable") {
|
|
28
29
|
return {
|
|
29
|
-
content: [
|
|
30
|
+
content: [
|
|
31
|
+
{ type: "text", text: `⚠ Crypto history unavailable for ${id} (${result.reason}).` },
|
|
32
|
+
],
|
|
30
33
|
details: [],
|
|
31
34
|
};
|
|
32
35
|
}
|
|
@@ -45,7 +48,25 @@ export const cryptoHistoryTool: AgentTool<typeof params, OHLCV[]> = {
|
|
|
45
48
|
)
|
|
46
49
|
.join("\n");
|
|
47
50
|
|
|
48
|
-
const
|
|
51
|
+
const riskLines = buildRiskLines(id, bars);
|
|
52
|
+
const text = [...summary, ...riskLines, "", "Recent bars:", table].join("\n");
|
|
49
53
|
return { content: [{ type: "text", text }], details: bars };
|
|
50
54
|
},
|
|
51
55
|
};
|
|
56
|
+
|
|
57
|
+
function buildRiskLines(id: string, bars: OHLCV[]): string[] {
|
|
58
|
+
if (bars.length < 30) return [];
|
|
59
|
+
const metrics = computeRiskMetrics(
|
|
60
|
+
id.toUpperCase(),
|
|
61
|
+
bars.map((bar) => bar.close),
|
|
62
|
+
);
|
|
63
|
+
return [
|
|
64
|
+
"",
|
|
65
|
+
"Risk metrics from crypto history:",
|
|
66
|
+
`Annualized Return: ${(metrics.annualizedReturn * 100).toFixed(2)}%`,
|
|
67
|
+
`Annualized Volatility: ${(metrics.annualizedVolatility * 100).toFixed(2)}%`,
|
|
68
|
+
`Sharpe Ratio: ${metrics.sharpeRatio.toFixed(2)}`,
|
|
69
|
+
`Max Drawdown: ${(metrics.maxDrawdown * 100).toFixed(2)}%`,
|
|
70
|
+
`Value at Risk (95%, daily): ${(metrics.var95 * 100).toFixed(2)}%`,
|
|
71
|
+
];
|
|
72
|
+
}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { Type } from "@sinclair/typebox";
|
|
2
1
|
import type { AgentTool } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import { Type } from "@sinclair/typebox";
|
|
3
3
|
import { getCryptoPrice } from "../../providers/coingecko.js";
|
|
4
4
|
import { wrapProvider } from "../../providers/wrap-provider.js";
|
|
5
5
|
import type { CryptoPrice } from "../../types/market.js";
|
|
6
6
|
|
|
7
7
|
const params = Type.Object({
|
|
8
8
|
id: Type.String({
|
|
9
|
-
description:
|
|
10
|
-
"CoinGecko coin ID (e.g. bitcoin, ethereum, solana, dogecoin). Use lowercase.",
|
|
9
|
+
description: "CoinGecko coin ID (e.g. bitcoin, ethereum, solana, dogecoin). Use lowercase.",
|
|
11
10
|
}),
|
|
12
11
|
});
|
|
13
12
|
|
|
14
13
|
export const cryptoPriceTool: AgentTool<typeof params, CryptoPrice> = {
|
|
15
14
|
name: "get_crypto_price",
|
|
16
15
|
label: "Crypto Price",
|
|
17
|
-
description:
|
|
18
|
-
"Get current crypto price, 24h change, market cap, volume, ATH, and supply data",
|
|
16
|
+
description: "Get current crypto price, 24h change, market cap, volume, ATH, and supply data",
|
|
19
17
|
parameters: params,
|
|
20
|
-
async execute(
|
|
18
|
+
async execute(_toolCallId, args) {
|
|
21
19
|
const result = await wrapProvider("coingecko", () => getCryptoPrice(args.id.toLowerCase()));
|
|
22
20
|
if (result.status === "unavailable") {
|
|
23
21
|
return {
|
|
24
|
-
content: [
|
|
22
|
+
content: [
|
|
23
|
+
{ type: "text", text: `⚠ Crypto price unavailable for ${args.id} (${result.reason}).` },
|
|
24
|
+
],
|
|
25
25
|
details: null as any,
|
|
26
26
|
};
|
|
27
27
|
}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import type { AgentTool } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import { type Static, Type } from "@sinclair/typebox";
|
|
3
|
+
import type { ScreenerRow, ScreenFilterOp } from "../../providers/tradingview.js";
|
|
4
|
+
import { screenStocks } from "../../providers/tradingview.js";
|
|
5
|
+
import { wrapProvider } from "../../providers/wrap-provider.js";
|
|
6
|
+
|
|
7
|
+
const filterOps = [
|
|
8
|
+
"greater",
|
|
9
|
+
"GREATER",
|
|
10
|
+
"gt",
|
|
11
|
+
"GT",
|
|
12
|
+
">",
|
|
13
|
+
"egreater",
|
|
14
|
+
"EGREATER",
|
|
15
|
+
"gte",
|
|
16
|
+
"GTE",
|
|
17
|
+
">=",
|
|
18
|
+
"less",
|
|
19
|
+
"LESS",
|
|
20
|
+
"lt",
|
|
21
|
+
"LT",
|
|
22
|
+
"<",
|
|
23
|
+
"eless",
|
|
24
|
+
"ELESS",
|
|
25
|
+
"lte",
|
|
26
|
+
"LTE",
|
|
27
|
+
"<=",
|
|
28
|
+
"equal",
|
|
29
|
+
"EQUAL",
|
|
30
|
+
"eq",
|
|
31
|
+
"EQ",
|
|
32
|
+
"==",
|
|
33
|
+
"nequal",
|
|
34
|
+
"NEQUAL",
|
|
35
|
+
"neq",
|
|
36
|
+
"NEQ",
|
|
37
|
+
"!=",
|
|
38
|
+
"in_range",
|
|
39
|
+
"IN_RANGE",
|
|
40
|
+
"not_in_range",
|
|
41
|
+
"NOT_IN_RANGE",
|
|
42
|
+
"crosses",
|
|
43
|
+
"CROSSES",
|
|
44
|
+
"crosses_above",
|
|
45
|
+
"CROSSES_ABOVE",
|
|
46
|
+
"crosses_below",
|
|
47
|
+
"CROSSES_BELOW",
|
|
48
|
+
"above%",
|
|
49
|
+
"ABOVE%",
|
|
50
|
+
"below%",
|
|
51
|
+
"BELOW%",
|
|
52
|
+
"match",
|
|
53
|
+
"MATCH",
|
|
54
|
+
"nmatch",
|
|
55
|
+
"NMATCH",
|
|
56
|
+
"has",
|
|
57
|
+
"HAS",
|
|
58
|
+
"has_none_of",
|
|
59
|
+
"HAS_NONE_OF",
|
|
60
|
+
"empty",
|
|
61
|
+
"EMPTY",
|
|
62
|
+
"nempty",
|
|
63
|
+
"NEMPTY",
|
|
64
|
+
] as const;
|
|
65
|
+
|
|
66
|
+
const filterOp = Type.String({ enum: [...filterOps] });
|
|
67
|
+
|
|
68
|
+
const params = Type.Object({
|
|
69
|
+
market: Type.Optional(
|
|
70
|
+
Type.String({ description: "TradingView market path segment. Default: america" }),
|
|
71
|
+
),
|
|
72
|
+
columns: Type.Optional(
|
|
73
|
+
Type.Array(
|
|
74
|
+
Type.String({
|
|
75
|
+
description:
|
|
76
|
+
"TradingView scanner field, optionally timeframe-qualified (for example RSI|60)",
|
|
77
|
+
}),
|
|
78
|
+
),
|
|
79
|
+
),
|
|
80
|
+
filter: Type.Optional(
|
|
81
|
+
Type.Array(
|
|
82
|
+
Type.Object({
|
|
83
|
+
field: Type.String({
|
|
84
|
+
description: "TradingView scanner field, optionally timeframe-qualified",
|
|
85
|
+
}),
|
|
86
|
+
op: filterOp,
|
|
87
|
+
value: Type.Optional(
|
|
88
|
+
Type.Unknown({ description: "Filter value for operations that require one" }),
|
|
89
|
+
),
|
|
90
|
+
}),
|
|
91
|
+
{ description: "Flat AND filter clauses; nested OR trees are not supported" },
|
|
92
|
+
),
|
|
93
|
+
),
|
|
94
|
+
sort: Type.Optional(
|
|
95
|
+
Type.Object({
|
|
96
|
+
field: Type.String({ description: "TradingView scanner field to sort by" }),
|
|
97
|
+
direction: Type.Optional(Type.String({ enum: ["asc", "ASC", "desc", "DESC"] })),
|
|
98
|
+
}),
|
|
99
|
+
),
|
|
100
|
+
limit: Type.Optional(
|
|
101
|
+
Type.Number({ description: "Maximum rows to return. Default 50; maximum 500" }),
|
|
102
|
+
),
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
export const screenStocksTool: AgentTool<typeof params, ScreenerRow[]> = {
|
|
106
|
+
name: "screen_stocks",
|
|
107
|
+
label: "Stock Screener",
|
|
108
|
+
description:
|
|
109
|
+
'Screen stocks across a market using TradingView scanner filters, columns, sorting, and row limits. Use for breadth queries like finding large caps, oversold stocks, movers, or filtered market lists; use quote/history tools for single-security quote or history requests. Common aliases are accepted: gt/gte/lt/lte for comparisons, market_cap for market_cap_basic, change_percent for change, and RSI|14D for RSI. For sector screens, use the sector field; technology usually means sector in_range ["Electronic Technology", "Technology Services"]. Use description for company names because name is the ticker.',
|
|
110
|
+
parameters: params,
|
|
111
|
+
async execute(_toolCallId, args) {
|
|
112
|
+
const normalized = normalizeArgs(args);
|
|
113
|
+
const result = await wrapProvider("tradingview", () =>
|
|
114
|
+
screenStocks({
|
|
115
|
+
market: normalized.market,
|
|
116
|
+
columns: normalized.columns,
|
|
117
|
+
filter: normalized.filter,
|
|
118
|
+
sort: normalized.sort,
|
|
119
|
+
limit: normalized.limit,
|
|
120
|
+
}),
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
if (result.status === "unavailable") {
|
|
124
|
+
return {
|
|
125
|
+
content: [
|
|
126
|
+
{
|
|
127
|
+
type: "text",
|
|
128
|
+
text: [
|
|
129
|
+
`Stock screening unavailable (${result.reason}).`,
|
|
130
|
+
"Manual fallback: run the same screen in TradingView or another screener with the requested filters and sort. Treat matches as candidates, not recommendations, and verify live quotes/news before acting.",
|
|
131
|
+
].join("\n"),
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
details: [],
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const rows = result.data;
|
|
139
|
+
if (rows.length === 0) {
|
|
140
|
+
return {
|
|
141
|
+
content: [
|
|
142
|
+
{
|
|
143
|
+
type: "text",
|
|
144
|
+
text: "No stocks matched the screen. TradingView scanner data may be delayed about 15 minutes and comes from an unofficial endpoint.",
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
details: rows,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const lines = [
|
|
152
|
+
`**Stock screen** — ${rows.length} TradingView result${rows.length === 1 ? "" : "s"}`,
|
|
153
|
+
...(result.stale ? [`⚠ Using cached TradingView screen from ${result.timestamp}.`] : []),
|
|
154
|
+
`Data freshness: ${result.stale ? `cached screen retrieved at ${result.timestamp}` : `retrieved at ${result.timestamp}`}; TradingView scanner data may be delayed about 15 minutes and comes from an unofficial endpoint.`,
|
|
155
|
+
formatInterpretationNote(normalized),
|
|
156
|
+
"",
|
|
157
|
+
...rows.map(formatRow),
|
|
158
|
+
];
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
162
|
+
details: rows,
|
|
163
|
+
};
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
function normalizeArgs(args: Static<typeof params>) {
|
|
168
|
+
return {
|
|
169
|
+
...args,
|
|
170
|
+
columns: args.columns
|
|
171
|
+
?.map(normalizeField)
|
|
172
|
+
.filter((field, index, fields) => fields.indexOf(field) === index),
|
|
173
|
+
filter: args.filter?.map((clause) => ({
|
|
174
|
+
...clause,
|
|
175
|
+
field: normalizeField(clause.field),
|
|
176
|
+
op: normalizeFilterOp(clause.op),
|
|
177
|
+
...(clause.value !== undefined && { value: normalizeValue(clause.value) }),
|
|
178
|
+
})),
|
|
179
|
+
sort: args.sort && {
|
|
180
|
+
...args.sort,
|
|
181
|
+
field: normalizeField(args.sort.field),
|
|
182
|
+
direction: normalizeSortDirection(args.sort.direction),
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function normalizeFilterOp(op: string): ScreenFilterOp {
|
|
188
|
+
const normalized = op.toLowerCase();
|
|
189
|
+
if (normalized === "gt" || normalized === ">") return "greater";
|
|
190
|
+
if (normalized === "gte" || normalized === ">=") return "egreater";
|
|
191
|
+
if (normalized === "lt" || normalized === "<") return "less";
|
|
192
|
+
if (normalized === "lte" || normalized === "<=") return "eless";
|
|
193
|
+
if (normalized === "eq" || normalized === "==") return "equal";
|
|
194
|
+
if (normalized === "neq" || normalized === "!=") return "nequal";
|
|
195
|
+
return normalized as ScreenFilterOp;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function normalizeSortDirection(direction?: string): "asc" | "desc" | undefined {
|
|
199
|
+
const normalized = direction?.toLowerCase();
|
|
200
|
+
if (normalized === "asc" || normalized === "desc") return normalized;
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function normalizeField(field: string): string {
|
|
205
|
+
const trimmed = field.trim();
|
|
206
|
+
const lower = trimmed.toLowerCase();
|
|
207
|
+
if (lower === "market_cap" || lower === "marketcap") return "market_cap_basic";
|
|
208
|
+
if (lower === "total_volume" || lower === "share_volume" || lower === "shares_traded")
|
|
209
|
+
return "volume";
|
|
210
|
+
if (
|
|
211
|
+
lower === "change_percent" ||
|
|
212
|
+
lower === "percent_change" ||
|
|
213
|
+
lower === "daily_change_percent" ||
|
|
214
|
+
lower === "day_change_percent" ||
|
|
215
|
+
lower === "price_change_percent"
|
|
216
|
+
)
|
|
217
|
+
return "change";
|
|
218
|
+
if (lower === "company_name" || lower === "company" || lower === "security_name")
|
|
219
|
+
return "description";
|
|
220
|
+
if (/^rsi(?:\|(?:14|14d|14d\|close|1d|d|daily))?$/i.test(trimmed)) return "RSI";
|
|
221
|
+
return trimmed;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function formatInterpretationNote(args: ReturnType<typeof normalizeArgs>): string {
|
|
225
|
+
const fields = [
|
|
226
|
+
...(args.columns ?? []),
|
|
227
|
+
...(args.filter?.map((clause) => clause.field) ?? []),
|
|
228
|
+
args.sort?.field,
|
|
229
|
+
].filter(Boolean);
|
|
230
|
+
const hasField = (field: string) => fields.includes(field);
|
|
231
|
+
const notes = ["Treat screen results as candidates, not recommendations."];
|
|
232
|
+
if (hasField("RSI"))
|
|
233
|
+
notes.push("RSI below 30 can mark oversold momentum, but it is not a buy signal by itself.");
|
|
234
|
+
if (args.sort?.field === "volume")
|
|
235
|
+
notes.push(
|
|
236
|
+
"Volume sorting shows the most actively traded matches rather than unusual volume by itself.",
|
|
237
|
+
);
|
|
238
|
+
if (hasField("change"))
|
|
239
|
+
notes.push(
|
|
240
|
+
"Daily percent change depends on the current market session; confirm market status and live quotes before acting.",
|
|
241
|
+
);
|
|
242
|
+
return `Interpretation note: ${notes.join(" ")}`;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function normalizeValue(value: unknown): unknown {
|
|
246
|
+
if (typeof value !== "string") return value;
|
|
247
|
+
const trimmed = value.trim().replace(/,/g, "");
|
|
248
|
+
if (/^[+-]?\d+(?:\.\d+)?$/.test(trimmed)) return Number(trimmed);
|
|
249
|
+
const short = /^\$?(\d+(?:\.\d+)?)\s*([kmbt])$/i.exec(trimmed);
|
|
250
|
+
if (short) return Number(short[1]) * multiplierFor(short[2]);
|
|
251
|
+
const long = /^\$?(\d+(?:\.\d+)?)\s*(thousand|million|billion|trillion)$/i.exec(trimmed);
|
|
252
|
+
if (long) return Number(long[1]) * multiplierFor(long[2]);
|
|
253
|
+
return value;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function multiplierFor(unit: string): number {
|
|
257
|
+
const normalized = unit.toLowerCase();
|
|
258
|
+
if (normalized === "k" || normalized === "thousand") return 1_000;
|
|
259
|
+
if (normalized === "m" || normalized === "million") return 1_000_000;
|
|
260
|
+
if (normalized === "b" || normalized === "billion") return 1_000_000_000;
|
|
261
|
+
return 1_000_000_000_000;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function formatRow(row: ScreenerRow): string {
|
|
265
|
+
const entries = Object.entries(row.values)
|
|
266
|
+
.filter(([field]) => field !== "name")
|
|
267
|
+
.slice(0, 8)
|
|
268
|
+
.map(([field, value]) => `${field}: ${formatValue(value)}`);
|
|
269
|
+
const suffix = entries.length > 0 ? ` — ${entries.join(" | ")}` : "";
|
|
270
|
+
return ` ${row.symbol} (${row.tvSymbol})${suffix}`;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function formatValue(value: unknown): string {
|
|
274
|
+
if (typeof value === "number")
|
|
275
|
+
return Number.isInteger(value) ? value.toLocaleString() : value.toFixed(2);
|
|
276
|
+
if (value === null || value === undefined) return "N/A";
|
|
277
|
+
if (Array.isArray(value)) return value.join(", ");
|
|
278
|
+
return String(value);
|
|
279
|
+
}
|