# OpenCandle full AI context Last updated: 2026-06-13 Canonical site: https://opencandle.app Repository: https://github.com/Kahtaf/OpenCandle Package: https://www.npmjs.com/package/opencandle OpenCandle is an open source financial investigator. It runs as a terminal agent and local browser GUI, gathers finance data through explicit tools, records provider gaps, and then synthesizes answers from gathered evidence. ## OpenCandle Docs Source: https://opencandle.app/docs/index.html # OpenCandle Docs OpenCandle is an open source financial investigator: a terminal agent and local browser workbench for gathering market evidence, inspecting tool output, and turning real provider data into research answers. It is built for investors, builders, and researchers who want the speed of an agent without hiding the source data. Tools fetch and format market, macro, options, fundamentals, filings, sentiment, and portfolio data. The model synthesizes after the evidence is gathered. OpenCandle is read-only research software. It does not place trades, route orders, or provide financial advice. ## How OpenCandle Works OpenCandle keeps a simple research loop: 1. You ask a financial question in the terminal or local GUI. 2. OpenCandle identifies the kind of investigation: quote lookup, comparison, portfolio review, options strategy, filing check, macro question, sentiment read, education, or state update. 3. It asks a focused follow-up only when a missing detail changes the answer. 4. It gathers tool-backed evidence and surfaces provider gaps or stale data. 5. It writes an answer that separates facts from judgment and names the important risks. Pi provides the bundled local agent runtime, model setup, sessions, and terminal shell. You do not need a separate Pi install for normal OpenCandle usage. OpenCandle adds the finance tools, workflows, provider integrations, local finance state, GUI cards, and evaluation harness. ## Start Here - [Getting Started](./getting-started.md) for install, setup, and first investigations. - [First Run](./first-run.md) for a five-minute path from install to a successful keyless market answer. - [TUI](./tui.md) for terminal usage, slash commands, sessions, and CLI-vs-GUI tradeoffs. - [Investigation Recipes](./investigation-recipes.md) for repeatable research paths. - [Data Sources](./data-sources.md) for provider coverage, optional keys, and local state. - [Configuration](./configuration.md) for env vars, file config, OpenCandle state files, and GUI runtime knobs. - [GUI Quickstart](./gui-quickstart.md) for the local browser workbench. - [System Architecture](./system-architecture.md) for how questions become investigations, evidence, and answers. - [Build a Tool](./build-a-tool.md) for adding first-party tools or external add-on packages. - [Testing and Evals](./testing-and-evals.md) for validating tools, workflows, GUI behavior, and full-session quality. - [Benchmarking](./benchmarking.md) for comparing OpenCandle against generic no-tool agents. ## What OpenCandle Investigates | Area | Examples | | --- | --- | | Market data | Quotes, price history, ticker lookup, crypto price and history | | Options | Option chains, open interest, implied volatility, locally computed Greeks | | Fundamentals | Company overview, financial statements, earnings, DCF, comparable companies | | Macro | FRED series, rates, inflation, GDP, unemployment, crypto Fear & Greed | | Sentiment | Reddit, Twitter/X local browser session, web search, cross-source sentiment summaries | | Filings | SEC EDGAR filing search | | Portfolio | Watchlists, holdings, correlation, prediction tracking, risk analysis | ## Operating Principles - Evidence first. OpenCandle should show the data it used and avoid unsupported conclusions. - Tools do not analyze. They fetch and format; analyst prompts and the model synthesize. - Provider gaps are visible. Missing keys, stale data, and degraded sources should be surfaced. - Local state stays local. OpenCandle user state lives under `~/.opencandle/` unless `OPENCANDLE_HOME` is set. - Contributions stay testable. Unit tests use fixtures and mocked fetch calls, not live APIs. ## When To Use OpenCandle Use OpenCandle when a question needs current or inspectable financial evidence: quotes, price history, options chains, filings, macro data, sentiment, watchlists, portfolio state, or a visible trail of what data was missing. OpenCandle can answer pure education questions without unnecessary tools, but it is most differentiated when evidence matters. It is designed for auditable market research, not for pretending every finance question needs a data lookup. ## Common Workflows ```bash opencandle opencandle gui npm run gui npm test npm run docs:site:build ``` Inside the agent, start with prompts like: ```text /analyze NVDA What is AAPL trading at? Compare MSFT and GOOGL using price, fundamentals, and sentiment Show me TSLA puts with Greeks Get the fed funds rate from FRED ``` OpenCandle is research software, not a financial advisor. It can gather and organize evidence, but final judgment and risk assessment remain with the user. --- ## Getting Started Source: https://opencandle.app/docs/getting-started.html # Getting Started OpenCandle runs as an interactive Pi agent in the terminal and as a local browser GUI. Pi is the bundled local agent runtime that handles model setup, the terminal shell, and saved sessions; OpenCandle adds the financial tools and workflows on top. The CLI is the primary entry point; the GUI is a local workbench for chat, tool discovery, provider status, session history, and financial context. OpenCandle is read-only research software. It does not place trades, route orders, or provide financial advice. ## Requirements - Node.js `^20.19.0`, `^22.12.0`, or `>=24.0.0 <27` - One supported model provider configured through Pi: OpenAI, Anthropic, or Google - Optional market data provider keys for expanded coverage ## Install ```bash npm install -g opencandle opencandle ``` You can also run the latest package without a global install: ```bash npx opencandle@latest ``` From a source checkout: ```bash npm install cp .env.example .env npm start ``` On Windows Command Prompt, use `copy .env.example .env` instead of `cp .env.example .env`. OpenCandle stores local state in `~/.opencandle` on macOS/Linux and `%USERPROFILE%\.opencandle` on Windows unless `OPENCANDLE_HOME` is set. On first run, OpenCandle walks through model setup. You can rerun setup later from inside the agent with `/setup`. For the fastest successful path, follow [First Run](./first-run.md). It shows a keyless first prompt, what success looks like, and how to handle common setup failures. ## Choose Your Interface Use the terminal UI when you want the fastest keyboard loop, slash commands, and a plain transcript. Use the GUI when you want a visual investigation workspace: chat, workflow cards, tool results, provider setup, session history, and financial context in one browser tab. Both use the same OpenCandle tools and saved session state. You can start in the terminal and later inspect sessions in the GUI. ## Run the Local GUI From a source checkout: ```bash npm run gui ``` From an installed package: ```bash opencandle gui ``` Then open `http://127.0.0.1:14567`. The GUI binds locally and shares Pi sessions through a writer/follower lock so only one process mutates a session at a time. Good first GUI flow: 1. Ask `What is AAPL trading at?` 2. Open the catalog with `⌘K` on macOS or `Ctrl+K` on Windows/Linux. 3. Pick a workflow such as Comprehensive Analysis or Compare Assets. 4. Inspect the tool card or drawer to see what data was used. 5. Open the provider tab if the answer says a data source is missing. See [GUI Quickstart](./gui-quickstart.md) for catalog usage, health checks, Tailscale access, and writer/follower behavior. ## Configure Providers Model credentials are handled by Pi. OpenCandle-specific provider keys can come from environment variables or `~/.opencandle/config.json`. | Key | Required | Used for | | --- | --- | --- | | `GEMINI_API_KEY` | No | Google models through Pi | | `OPENAI_API_KEY` | No | OpenAI models through Pi | | `ANTHROPIC_API_KEY` | No | Anthropic models through Pi | | `ALPHA_VANTAGE_API_KEY` | No | Fundamentals, earnings, financial statements | | `FRED_API_KEY` | No | Macro series such as rates, CPI, GDP, unemployment | | `BRAVE_API_KEY` | No | Brave web search fallback | | `EXA_API_KEY` | No | Exa web search | | `FINNHUB_API_KEY` | No | Finnhub company news for sentiment summaries | Yahoo Finance, CoinGecko, Reddit, SEC EDGAR, DuckDuckGo search, and the alternative.me crypto Fear & Greed index do not require keys. Example config: ```json { "providers": { "alphaVantage": { "apiKey": "..." }, "fred": { "apiKey": "..." }, "brave": { "apiKey": "..." }, "exa": { "apiKey": "..." }, "finnhub": { "apiKey": "..." } } } ``` Environment variables override `~/.opencandle/config.json`. Set `OPENCANDLE_HOME` to store OpenCandle state somewhere other than `~/.opencandle/`. See [Configuration](./configuration.md) for the complete env var list, config precedence, state files, and GUI host/port settings. ## First Investigations ```text What is AAPL trading at? Run /analyze NVDA Compare BTC and ETH over the last month Show MSFT puts with Greeks Get CPI from FRED Add 100 shares of NVDA at 120 to my portfolio, then show my portfolio ``` OpenCandle should tell you when a provider is unavailable, a key is missing, or a result is degraded. Treat those warnings as part of the answer. For slash commands, session behavior, and CLI-vs-GUI tradeoffs, see [TUI](./tui.md). ## Validate a Checkout ```bash npm test npm run gui:web:build npm run docs:site:build ``` Use `npm run test:e2e` and `npm run test:e2e:providers` only when you intentionally want tests that hit live providers. --- ## First Run Source: https://opencandle.app/docs/first-run.html # First Run This path assumes you want the fastest successful run: connect one AI model, ask a keyless market-data prompt, and only add data-provider keys when OpenCandle tells you they would improve the answer. Model credentials and market-data provider keys are separate. OpenCandle needs a model before chat can start. A keyless market-data prompt means the market data source does not need an OpenCandle provider key; it does not mean the agent can run without model access. Pi is the bundled local agent runtime that handles model sign-in, model keys, the terminal shell, and saved sessions. OpenCandle uses Pi for that runtime layer and adds the finance-specific tools and GUI. ## Five-Minute Path 1. Install and start OpenCandle. ```bash npx opencandle@latest ``` From a source checkout, use: ```bash npm install npm start ``` 2. Connect an AI model when the setup prompt opens. Choose sign-in or paste an API key. OpenCandle needs a model before chat can start. After a successful connection, it selects a fast default model when one is available; otherwise it asks you to choose a model. 3. Start with a keyless market-data prompt. ```text What is AAPL trading at? Compare BTC and ETH over the last month What's r/wallstreetbets saying about META? ``` Yahoo Finance, CoinGecko, Reddit, SEC EDGAR, DuckDuckGo search, and the alternative.me crypto Fear & Greed index work without OpenCandle-specific provider keys. 4. Add provider keys only when needed. If an answer says a provider is missing or degraded, follow the suggested `/connect ...` command. Common examples: ```text /connect financials /connect economy /connect search ``` ## What Success Looks Like A good first answer should show that OpenCandle gathered evidence before synthesizing. For a quote prompt, expect a current price, daily move, timestamp or source context, and any caveats about availability. For a comparison prompt, expect per-asset data plus a short synthesis. If a provider was unavailable, the answer should say what was missing instead of pretending the data was complete. OpenCandle is research software, not a financial advisor. Treat warnings, stale-data notes, and data gaps as part of the output. ## Model Setup Expectations OpenCandle uses Pi for model credentials and model selection. You can connect with sign-in or with an API key for OpenAI, Anthropic, or Google models. Model credentials are stored by Pi; OpenCandle data-provider keys are separate and live in environment variables or `~/.opencandle/config.json`. Use `/setup` later if you want to reconnect auth or choose a different model setup path. Use `/model` when you only want to switch among models that are already available. ## Common Setup Failures | Symptom | What to do | | --- | --- | | Setup exits before chat starts | Start OpenCandle again and complete model setup. Chat requires a connected model. | | No models appear after adding a key | Check that the key matches the selected provider, then rerun `/setup`. | | A provider key was rejected | Re-run the suggested `/connect ...` command and paste a fresh key. Rejected keys are not saved. | | `/connect` says a provider is set by an environment variable | Update or unset that environment variable in your shell. Environment variables override `~/.opencandle/config.json`. | | Fundamentals, macro, or premium news are missing | Connect the matching data provider. Alpha Vantage covers many fundamentals, FRED covers macro series, and Finnhub/Brave/Exa expand news or search coverage. | | The GUI is open but not updating | Use the terminal session that owns the writer lock, or restart the GUI and reopen `http://127.0.0.1:14567`. | --- ## TUI Source: https://opencandle.app/docs/tui.html # TUI The terminal UI is the main OpenCandle agent experience. It runs chat, setup, slash commands, tool calls, and session state in one place. OpenCandle runs on Pi, the local agent runtime that provides the terminal UI, model auth, session storage, slash commands, and extension hooks. OpenCandle contributes the finance-specific tools, workflows, prompts, and local state. Start it with: ```bash opencandle ``` From a source checkout: ```bash npm start ``` ## Basics Type a question and press Enter. OpenCandle identifies what kind of financial investigation you are asking for, gathers provider-backed evidence when useful, and then asks the model to synthesize. Tools fetch and format evidence; the model writes the answer. If a ticker, goal, horizon, budget, or risk preference is missing and materially changes the answer, OpenCandle may ask a focused follow-up before continuing. If a provider is missing, stale, or unavailable, the answer should name that gap instead of hiding it. Good first prompts: ```text What is AAPL trading at? /analyze NVDA Compare MSFT and GOOGL using price, fundamentals, and sentiment Show me TSLA puts with Greeks Get the fed funds rate from FRED ``` Slash commands are optional. Plain-English prompts can trigger the same investigation paths: ```text Analyze NVDA and tell me whether to buy, wait, or avoid. I already own VOO and QQQ. Would SCHD diversify me? I own 200 shares of AMD. What protective put should I consider? Is this SPY/MSFT retirement portfolio too risky? ``` If a provider key would improve the result, OpenCandle should name the gap and suggest a `/connect ...` command. ## Slash Commands | Command | Use it for | | --- | --- | | `/setup` | Re-run AI model setup. Use this when chat cannot start, auth changed, or you want a different setup path. | | `/login` | Sign in to a model provider through Pi when supported by your local Pi install. | | `/model` | Switch between models that are already available through Pi. | | `/connect` | Connect OpenCandle data providers such as Alpha Vantage, FRED, Finnhub, Brave, or Exa. Run it bare for a picker, or pass a provider/category such as `/connect economy`. | | `/analyze ` | Run the multi-analyst stock workflow for one ticker, for example `/analyze NVDA`. | `/setup` and `/model` are about the AI model. `/connect` is about market-data providers. Keeping those separate makes setup easier to debug. ## Sessions OpenCandle stores session history through Pi and keeps OpenCandle user state under `~/.opencandle/` unless `OPENCANDLE_HOME` is set. A session can include normal chat messages, slash-command output, tool results, provider-gap notes, and the always-visible financial disclaimer. The local GUI reads the same Pi session state. GUI servers coordinate with a writer/follower lock so only one GUI process mutates a session directory at a time. The terminal UI shares the session files, but it does not acquire the GUI writer lock. If two views disagree, keep using the one that is actively accepting new messages and refresh the other. ## CLI vs GUI Use the TUI when you want the fastest keyboard-driven agent loop, setup commands, or a plain transcript in your terminal. Use the GUI when you want a local browser workbench: chat history, tool catalog, provider status, session navigation, and richer cards for market data. Start it with: ```bash opencandle gui ``` From a source checkout: ```bash npm run gui ``` Then open `http://127.0.0.1:14567`. The GUI is local-only and shares the same underlying sessions as the terminal. --- ## Investigation Recipes Source: https://opencandle.app/docs/investigation-recipes.html # Investigation Recipes OpenCandle works best when prompts name the evidence you want, not only the conclusion you hope to reach. These recipes are starting points for repeatable investigations. You can run these recipes as natural-language chat prompts, slash commands where available, or GUI catalog workflows. The GUI catalog pre-fills structured prompts, but the investigation still appears in the normal chat timeline. ## Stock Snapshot Use this when you want a quick but inspectable view of one public company. In the GUI, start from Comprehensive Analysis. ```text /analyze NVDA ``` Expected evidence: - current quote and recent price context - company overview or fundamentals when a keyed provider is available - relevant news or web context when search is configured - clear warnings for unavailable providers Sample shape: ```text Evidence gathered - Quote: provider timestamp and recent move shown before synthesis - Fundamentals: company profile or provider warning - Filings/news: source links or explicit "not configured" note Answer - What the evidence says - What still depends on judgment - Main downside risks and data gaps ``` Good follow-up: ```text What parts of that answer are based on stale or missing data? ``` ## Compare Assets Use this when choosing between two or more tickers. In the GUI, start from Compare Assets. ```text Compare MSFT, GOOGL, and AMZN using price trend, fundamentals, and sentiment. ``` Expected evidence: - normalized quote/history context for each symbol - comparable-company or financial-statement data when available - sentiment or web search context if configured - a synthesis that separates data-backed observations from judgment calls Sample shape: ```text Side-by-side comparison | Asset | Price context | Fundamental context | Sentiment/source notes | | --- | --- | --- | --- | | MSFT | provider quote + trend | available or unavailable | source count or gap | | GOOGL | provider quote + trend | available or unavailable | source count or gap | | AMZN | provider quote + trend | available or unavailable | source count or gap | Verdict - Best fit for the stated goal - Concentration, valuation, and data-quality risks ``` ## Options Screen Use this when exploring contracts and Greeks. In the GUI, start from Options Screener. ```text Show me TSLA puts expiring next month with Greeks and open interest. ``` Expected evidence: - available expirations and matching option-chain rows - implied volatility, open interest, and volume when provider data includes them - locally computed Greeks - missing-expiration or missing-symbol clarification when needed ## SEC Filing Trail Use this when you need primary-source company disclosure context. ```text Find recent SEC filings for AAPL and summarize what each filing type is for. ``` Expected evidence: - SEC EDGAR filing records - filing dates, accession links, or available identifiers - cautious synthesis that does not overstate what was inspected Sample shape: ```text SEC filing trail - 10-K/10-Q/8-K record: filing date, form type, accession or document link - What that form is normally used for - What OpenCandle inspected versus what would require reading the full filing ``` ## Macro Check Use this when a market question depends on rates, inflation, labor data, or growth. ```text Get the latest fed funds rate and compare it with CPI and unemployment. ``` Expected evidence: - FRED series values when `FRED_API_KEY` is configured - dates for the reported observations - warning if a requested series is missing or unavailable ## Sentiment Triangulation Use this when market narrative matters and you want cross-source evidence. ```text Give me a sentiment read on AMD across Reddit, Twitter/X, and web results. ``` Expected evidence: - source-specific sentiment records where available - web search results through Exa, Brave, or DuckDuckGo fallback - divergence notes when retail chatter and news context disagree - explicit provider/setup warnings ## Portfolio Risk Use this when checking local holdings or a hypothetical allocation. In the GUI, use chat for existing holdings or Portfolio Builder when you want OpenCandle to build a proposed allocation from goals and constraints. ```text Add 100 shares of NVDA at 120 and 50 shares of MSFT at 430, then run risk analysis. ``` Expected evidence: - locally stored portfolio entries - current market data used for valuation - concentration and correlation output where available - risk language that calls out downside scenarios ## Prompting Pattern For stronger answers, ask for the evidence path: ```text Analyze NVDA. Use quote, fundamentals, recent filings, sentiment, and macro context if available. Tell me which sources were unavailable before giving the synthesis. ``` That phrasing gives the agent permission to gather broadly while keeping provider gaps visible. --- ## Data Sources Source: https://opencandle.app/docs/data-sources.html # Data Sources OpenCandle combines free public sources, optional keyed APIs, and local state. Tools gather data and return structured details. The model can synthesize those details, but tool implementations should not invent financial conclusions or hardcode market numbers. ## Provider Coverage | Domain | Tools | Providers | | --- | --- | --- | | Market | `search_ticker`, `screen_stocks`, `get_stock_quote`, `get_stock_history` | Yahoo Finance; TradingView scanner for breadth screening and watchlist batch quotes; Alpha Vantage fallback for quote/history when configured | | Crypto | `get_crypto_price`, `get_crypto_history` | CoinGecko | | Options | `get_option_chain` with Greeks computed inside the result | Yahoo Finance plus local calculations | | Fundamentals | `get_company_overview`, `get_financials`, `get_earnings`, `compute_dcf`, `compare_companies` | Alpha Vantage | | Macro | `get_economic_data`, `get_fear_greed` | FRED, alternative.me crypto Fear & Greed | | Technical | `get_technical_indicators`, `backtest_strategy` | Local calculations over market history | | Sentiment | `get_reddit_sentiment`, `get_twitter_sentiment`, `search_web`, `get_web_sentiment`, `get_sentiment_summary`, `get_sentiment_trend` | Reddit JSON API, Twitter/X local browser session, Finnhub, DuckDuckGo, Brave, Exa | | Filings | `get_sec_filings` | SEC EDGAR | | Portfolio | `track_portfolio`, `analyze_risk`, `manage_watchlist`, `analyze_correlation`, `track_prediction` | Local state plus market providers | ## Keyed and Keyless Sources Keyless by default: - Yahoo Finance - TradingView scanner (unofficial, delayed scanner endpoint; used read-only and batch-first) - CoinGecko - Reddit JSON API - SEC EDGAR - DuckDuckGo search - alternative.me crypto Fear & Greed Optional keys: - `ALPHA_VANTAGE_API_KEY` expands fundamentals, earnings, financial statements, DCF, and company comparison coverage. - `FRED_API_KEY` enables macro series lookups. - `BRAVE_API_KEY` enables Brave as a web search fallback. - `EXA_API_KEY` enables Exa web search. - `FINNHUB_API_KEY` enables Finnhub company news in sentiment summaries. - Search and social providers can degrade based on available credentials, local browser login state, and provider health. Twitter/X sentiment requires a local browser session. ## Caching and Degradation External provider calls should use OpenCandle's shared cache and rate limiter. When a provider fails, tools should prefer a clear degraded response over pretending the data is fresh. Expected behavior: - Fresh data is returned when the provider succeeds. - Stale cache can be used when the provider is temporarily unavailable. - Missing credentials are reported as setup gaps. - Circuit breakers avoid repeatedly calling failing providers. - Unit tests mock provider responses with fixtures. TradingView scanner data is keyless but unofficial and can be delayed by about 15 minutes. `screen_stocks` is intended for broad filtered scans such as market movers, oversold lists, or large-cap screens. Single-security quotes, history, options, and company analysis should still use the Yahoo-backed quote/history tools and the fundamentals/options workflow tools. Watchlist checks use TradingView batch quotes for equity-like symbols and fill unresolved or unsupported symbols through Yahoo. The TradingView scanner implementation reimplements request grammar and decoding discipline in TypeScript rather than copying source. Reference projects used for protocol cross-checking include `himself65/finance-skills`, `shner-elmo/TradingView-Screener`, `deepentropy/tvscreener`, `Fynnius/TradingView.Screener`, and `ryar001/tradingview-screener-wrapper`; confirm upstream licenses before adapting non-trivial source. ## Local State OpenCandle user state defaults to `~/.opencandle/`. Pi configuration is separate and stays in `.pi/` or `~/.pi/agent/`. The CLI and GUI should not depend on repo-local `.pi/extensions/`. This keeps installed-package usage, source checkouts, and local GUI sessions from relying on accidental development artifacts. ## Safety Boundary OpenCandle does not guarantee completeness, accuracy, or suitability for trading decisions. It is designed to collect and organize research evidence. It should call out missing data, stale data, downside scenarios, and provider limitations instead of smoothing them over. --- ## Configuration Source: https://opencandle.app/docs/configuration.html # Configuration OpenCandle reads configuration from three places: 1. A `.env` file in the current working directory loaded at startup. 2. Process environment variables. 3. The OpenCandle JSON config file at `$OPENCANDLE_HOME/config.json`. The default OpenCandle home is `~/.opencandle`. Set `OPENCANDLE_HOME` to move user state and file config elsewhere. Relative `OPENCANDLE_HOME` values are resolved to absolute paths from the current working directory. ## Precedence Startup calls `loadEnv()` first, so values from `.env` are copied into `process.env`. The loader overwrites an existing key if the same key is already exported in the shell. Runtime config then resolves `process.env` before file config values. Effective precedence: 1. Values from `.env`. 2. Already-exported process env for keys not set in `.env`. 3. `$OPENCANDLE_HOME/config.json`. 4. Built-in defaults. For provider API keys and `OPENCANDLE_DEBATE`, env wins over JSON config. `OPENCANDLE_HOME`, `OPENCANDLE_GUI_HOST`, `OPENCANDLE_GUI_PORT`, and developer diagnostic switches are env-only. ## Environment Variables Most users only need model credentials, optional data-provider keys, the OpenCandle home directory, and GUI host/port settings. | Variable | Default | Purpose | | --- | --- | --- | | `GEMINI_API_KEY` | unset | Google model credential used by Pi model setup and the GUI setup panel. | | `OPENAI_API_KEY` | unset | OpenAI model credential used by Pi model setup and the GUI setup panel. | | `ANTHROPIC_API_KEY` | unset | Anthropic model credential used by Pi model setup and the GUI setup panel. | | `ALPHA_VANTAGE_API_KEY` | unset | Fundamentals, earnings, financial statements, DCF, and comps. Overrides `providers.alphaVantage.apiKey`. | | `FRED_API_KEY` | unset | FRED macro series. Overrides `providers.fred.apiKey`. | | `BRAVE_API_KEY` | unset | Brave search in the web-search cascade. Overrides `providers.brave.apiKey`. | | `EXA_API_KEY` | unset | Exa search. Overrides `providers.exa.apiKey`. | | `FINNHUB_API_KEY` | unset | Finnhub company news for sentiment summaries. Overrides `providers.finnhub.apiKey`. | | `OPENCANDLE_HOME` | `~/.opencandle` | Directory for OpenCandle config, local state, and browser profile data. | | `OPENCANDLE_DEBATE` | `true` | Enables adversarial bull/bear debate for comprehensive analysis. Set `false` or `0` to disable. | | `OPENCANDLE_GUI_HOST` | `127.0.0.1` | GUI bind host. Set `0.0.0.0` only when you intentionally want LAN/Tailscale access. | | `OPENCANDLE_GUI_ALLOW_REMOTE_PRIVATE_API` | unset | Allow the GUI's private market-state API to accept cookie-authenticated requests from non-loopback peers. Set `1` only together with an intentional `OPENCANDLE_GUI_HOST` network bind. | | `OPENCANDLE_GUI_PORT` | `14567` | GUI HTTP/WebSocket port. | | `OPENCANDLE_NOTIFICATION_WEBHOOK_URL` | unset | Optional local webhook target for alert/report notification delivery attempts. In-app notifications are still recorded first. | Run `opencandle monitor` to keep local alert/report automations active from a foreground terminal process without opening the GUI. Use `opencandle monitor --once` for a single local automation heartbeat. ### Advanced Developer Diagnostics These settings are for debugging request understanding and tool availability. Keep the defaults for normal use. | Variable | Default | Purpose | | --- | --- | --- | | `OPENCANDLE_ROUTER_MODE` | `rules` | Advanced request-understanding mode. Set `llm` to opt into the LLM router during development or eval runs. Invalid values fail startup config loading. | | `OPENCANDLE_TOOL_SCOPE_MODE` | `observe` | Tool-scope diagnostic mode. `observe` records selected bundles and active-tool candidates; `enforce` applies Pi active tools for each turn. Invalid values fail startup config loading. | | `OPENCANDLE_PLANNING_MIGRATION_STATUSES` | unset | Comma-separated planning rollout overrides in `task_family=status` form, for example `single_asset_decision=dual_run,asset_compare=observe_only`. Invalid entries fail startup config loading. | | `OPENCANDLE_AUTOMATION_HEARTBEAT_MS` | `60000` | GUI automation heartbeat interval in milliseconds. Values below `5000` or invalid values fall back to the default. | ## File Config `$OPENCANDLE_HOME/config.json` stores provider keys saved by `/connect` or the GUI provider setup flow. Supported fields: ```json { "providers": { "alphaVantage": { "apiKey": "..." }, "fred": { "apiKey": "..." }, "brave": { "apiKey": "..." }, "exa": { "apiKey": "..." }, "finnhub": { "apiKey": "..." } }, "debate": true, "sentiment": { "retentionDays": 30, "defaultSubreddits": ["wallstreetbets", "stocks", "investing", "options"], "commentsPerPost": 5, "divergenceThreshold": 0.4 } } ``` Sentiment keys are file-config only. Missing sentiment fields use the defaults shown above. ## OpenCandle Home State All paths below are rooted at `$OPENCANDLE_HOME`: | Path | Purpose | | --- | --- | | `config.json` | OpenCandle provider config and file-backed settings. | | `onboarding.json` | Provider setup, snooze, never-ask, and welcome state. | | `state.db` | SQLite store for memory/workflow rows plus user market state: instruments, aliases, watchlists, portfolio lots, prediction records, alert rules/events, report history, and import provenance. | | `sentinel.db` | Sentiment trend store. | | `browser-profile/` | Browser profile data used by Twitter/X login flows. | | `logs/` | Reserved OpenCandle log directory. | Watchlists, portfolios, and predictions are not loaded from `watchlist.json`, `portfolio.json`, or `predictions.json`. Those filenames are intentionally unsupported state sources; OpenCandle uses `state.db` for durable market state. Pi runtime config and sessions remain separate under Pi's own agent directory. OpenCandle does not move Pi state into `$OPENCANDLE_HOME`. ## GUI Runtime Run the GUI with: ```bash npm run gui ``` By default it listens on `http://127.0.0.1:14567`. The health endpoint is: ```bash curl http://127.0.0.1:14567/health ``` It returns `{"ok":true,"role":"writer"}` or `{"ok":true,"role":"follower"}`. `ok` means the HTTP server is alive. `role` describes whether this process acquired the Pi session writer lock. Writer processes can mutate the session: chat runs, tool toggles, provider/model setup, and session create/open/rename/delete. Follower processes serve read APIs and the browser app but reject mutating actions with "Read-only follower mode". Only one process should be writer for a session directory. A lock with a live process and fresh heartbeat stays authoritative; stale locks are recovered after the grace window. --- ## GUI Quickstart Source: https://opencandle.app/docs/gui-quickstart.html # OpenCandle GUI Quickstart 1. Install dependencies from the repo root with `npm install`. 2. Start the local GUI with `npm run gui` from a checkout, or `opencandle gui` from an installed package. 3. Open `http://127.0.0.1:14567`. 4. If the model setup panel appears, connect a model first. Chat cannot run without model access. 5. Start with a keyless market-data prompt such as `What is AAPL trading at?`, then try `/analyze NVDA` or the empty-state action cards. 6. Open the catalog with `⌘K` on macOS, `Ctrl+K` on Windows/Linux, or the top-bar catalog button. Use Tools to run a single tool, Workflows to submit a workflow prompt, and Providers to inspect missing credentials. The GUI binds to `127.0.0.1:14567` by default. Override with `OPENCANDLE_GUI_HOST` and `OPENCANDLE_GUI_PORT`; set `OPENCANDLE_GUI_HOST=0.0.0.0` only when you intentionally want LAN or Tailscale access. The GUI shares Pi sessions through a writer/follower lock so only one process mutates a session at a time. Pi is the bundled local agent runtime that owns model setup and saved sessions; the writer/follower lock is the GUI's guard against two browser servers editing the same session at once. The writer can run chat, save provider/model setup, toggle tools, and manage sessions. Followers can serve the browser and read session state, but mutating actions return "Read-only follower mode". Check the running role with: ```bash curl http://127.0.0.1:14567/health ``` `{"ok":true,"role":"writer"}` means this process owns the writer lock. `{"ok":true,"role":"follower"}` means the server is healthy but another live process owns the lock. ## Tailscale Access For remote viewing, keep the local GUI running and expose it with Tailscale Serve from the machine that is running OpenCandle. Use your own Tailscale node address or hostname: ```bash tailscale serve --bg http://127.0.0.1:14567 ``` Depending on your Tailscale setup, the shared URL is shown by `tailscale serve status`. If the page returns `502`, the tunnel is up but the local GUI is not listening. Restart `npm run gui` or `opencandle gui` and verify `curl http://127.0.0.1:14567/health` returns `{"ok":true,...}`. If it returns `role:"follower"`, stop the existing writer or use the follower for read-only viewing. ## Investigator Workflow The GUI is a local investigation workbench. It keeps the transcript, tool catalog, provider setup, session history, and financial context close together so the user can see what evidence the agent is using. - Chat carries the question, tool calls, and synthesis. - Catalog exposes workflows, individual tools, and provider setup without guessing prompt syntax. - Session history keeps prior investigations reachable through Pi session state. - Context and tool result cards make prices, filings, macro data, sentiment, and portfolio facts inspectable. - Writer/follower locking keeps one local process responsible for mutating a session. ## What You Can Do From The GUI - Ask a normal finance question, such as `Should I add NVDA if I already own AAPL and TSLA?` - Launch a workflow from the catalog, such as Comprehensive Analysis, Compare Assets, Portfolio Builder, or Options Screener. - Run one tool directly when you only need a quote, option chain, filing lookup, or macro series. - Connect provider keys from the Providers tab instead of editing config files. - Inspect tool cards and the drawer to see arguments, results, sources, and warnings. - Reopen previous sessions and continue the investigation. - Answer focused follow-up questions when OpenCandle needs a ticker, goal, horizon, budget, or risk preference before proceeding. Workflow catalog entries prefill a structured chat prompt. They do not switch the GUI into a separate mode; the result still appears in the same chat timeline with the same tool cards and session history. The financial context panel summarizes relevant local context from the current session, such as watchlist entries, holdings, recent symbols, and provider status. Treat it as a navigation aid, not a separate source of truth. ## When To Use The GUI Use the GUI when you want to inspect tool output visually, browse prior sessions, run an individual tool from the catalog, or see provider setup status without remembering command syntax. Use the TUI when you want the fastest keyboard loop, slash commands, or a plain terminal transcript. See [TUI](./tui.md). For GUI internals, see [System Architecture](./system-architecture.md). For GUI validation, see [Testing and Evals](./testing-and-evals.md). --- ## System Architecture Source: https://opencandle.app/docs/system-architecture.html # System Architecture OpenCandle is a local financial research workbench. You ask a question in the terminal or the browser GUI; OpenCandle figures out what kind of investigation it is, gathers evidence from finance tools, keeps the trace visible, and produces an answer that names risks and data gaps. It is not an automated trading system and it is not a financial advisor. It is research software built to make the evidence path inspectable. ## The Everyday Flow ```text User question | v Understand the financial task - symbols, companies, assets, portfolio details - time horizon, risk profile, budget, strategy, or missing details - whether this is education, comparison, portfolio review, options, sentiment, filings, macro, or state tracking | v Choose an investigation path - use a structured workflow when the user asks for one - otherwise prepare a finance-specific evidence plan for the question - ask a focused follow-up only when the missing detail changes the answer | v Gather tool-backed evidence - quotes, histories, options chains, fundamentals, filings, macro data - sentiment, web/news context, portfolio state, risk, correlations, backtests - provider freshness, missing credentials, stale cache, or degraded data | v Produce the answer - cite what was actually checked - separate facts from judgment - call out uncertainty and downside scenarios - answer directly when the user asks for a decision or tradeoff ``` ## User Interfaces OpenCandle has two main surfaces. The terminal UI is the fastest keyboard loop. It supports normal chat, slash commands, model setup, provider connection, and saved Pi sessions. The local GUI is a browser workbench at `http://127.0.0.1:14567`. It shows chat, session history, provider setup, a tool and workflow catalog, a financial context panel, and visual result cards for market data, options, macro, filings, sentiment, and portfolio facts. Both surfaces use the same OpenCandle session and finance tools. The GUI adds richer rendering and easier discovery; it is not a separate agent. ## Workflows And Regular Questions Some prompts map cleanly to visible workflows: | Workflow | Use it for | | --- | --- | | Comprehensive Analysis | A broad single-asset investigation such as `/analyze NVDA`. | | Compare Assets | Side-by-side comparison of stocks, ETFs, crypto assets, or funds. | | Portfolio Builder | Building a proposed allocation from goals, budget, horizon, and risk preference. | | Options Screener | Looking at calls, puts, covered calls, protective puts, expirations, and Greeks. | Regular chat questions still get structure. For example: - "Does adding NVDA make sense if I already own AAPL and TSLA?" - "Is this SPY/MSFT retirement portfolio too risky?" - "Is ARMH still the right ticker for Arm?" - "Should I keep cash in HYSA, T-bills, CDs, or a bond ETF?" Those are not just freeform replies. OpenCandle still extracts the relevant entities, chooses useful evidence, asks for clarification when needed, and applies the right answer shape for the task. ## Clarifying Questions OpenCandle should ask a follow-up only when the missing information materially changes the investigation. Good examples: - An unknown ticker appears in an earnings-risk question. - An options request is missing the underlying position. - A portfolio-construction request has no budget, horizon, or risk preference. In the GUI, these appear as question cards in the chat. After you answer, OpenCandle continues the investigation and uses tools; it should not stop at a generic response. ## Tools And Providers Tools are small finance capabilities. They fetch and format data. They should not invent market facts or make the final investment conclusion. | Area | Examples | Providers or source | | --- | --- | --- | | Market data | quotes, history, ticker lookup, crypto price/history | Yahoo Finance, Alpha Vantage when configured, CoinGecko | | Options | option chains, open interest, implied volatility, Greeks | Yahoo Finance plus local calculations | | Fundamentals | company overview, financials, earnings, DCF, comparisons | Alpha Vantage | | Macro | rates, CPI, GDP, unemployment, fear/greed | FRED, alternative.me | | Technical | indicators, moving-average backtests | Local calculations over market history | | Sentiment | Reddit, Twitter/X, web/news sentiment, source summaries | Reddit, local browser session, Finnhub, Exa, Brave, DuckDuckGo | | Filings | SEC filing search | SEC EDGAR | | Portfolio | holdings, watchlists, predictions, risk, correlation | Local state plus market data | Provider helpers add caching, rate limiting, fallback behavior, and degraded-state metadata. If a provider is missing, stale, or unavailable, that should show up in the result instead of being hidden. ## Evidence And Answer Quality OpenCandle answers should be useful because the evidence is visible and the answer shape matches the question. Expected behavior: - A current-price question should show the quote source and freshness. - A portfolio-risk question should discuss concentration, horizon, drawdown, and simple adjustments. - An options question should distinguish per-share option quotes from standard 100-share contract cost. - A ticker mismatch should be treated as a red flag before discussing social hype. - A filing question should separate SEC filing evidence from news or market context. - A pure education question should avoid unnecessary tool calls. The model synthesizes after evidence is gathered. It should answer directly, name risks, disclose gaps, and avoid unsupported certainty. ## GUI Runtime The GUI server serves the built browser app, reads the current Pi session, and streams chat/session updates. Useful local endpoints: - `GET /health` returns whether the process is alive and whether it is the session `writer` or a read-only `follower`. - `GET /api/bootstrap` returns the initial catalog, setup state, sessions, prompts, and current snapshot. - `GET /api/sessions` lists saved sessions. - `GET /api/session/events` returns the current projected chat events. - `POST /api/chat/run` streams one chat run. - `GET /ws` provides live updates for setup, catalog, session, and ask-user events. Only one GUI process writes to a session at a time. A writer can run chat, answer follow-ups, save provider/model setup, toggle tools, and manage sessions. Followers can view the same state but cannot mutate it. ## Local State OpenCandle state defaults to `~/.opencandle/`. Common files: - `config.json` for provider keys and file-backed settings. - `state.db` for memory, workflow state, and durable user market state such as instruments, watchlists, portfolio lots, predictions, alerts, report runs, and import provenance. - `sentinel.db` for sentiment trend state. - `browser-profile/` for browser-backed sentiment flows such as Twitter/X. OpenCandle does not treat `watchlist.json`, `portfolio.json`, or `predictions.json` as supported state sources. Pi owns its own runtime config and session storage separately. OpenCandle should not depend on repo-local `.pi/extensions/` artifacts. ## Validation OpenCandle uses layered validation: - Unit tests for deterministic logic, mocked providers, and GUI state helpers. - End-to-end tests for CLI, credential flows, and live provider/tool behavior when needed. - Browser smoke tests for the local GUI. - Full-session evals that check whether the agent chose the right investigation path, used relevant tools, disclosed gaps, framed risk, and answered the user directly. - Competitive evals that compare OpenCandle against generic agents on realistic finance prompts. See [Testing and Evals](./testing-and-evals.md) and [Benchmarking](./benchmarking.md). --- ## Build a Tool Source: https://opencandle.app/docs/build-a-tool.html # Build an OpenCandle Investigation Tool Add a new data tool to OpenCandle by submitting a PR. A good OpenCandle tool behaves like an investigator's instrument: it fetches evidence, preserves source/freshness context, formats the result clearly, and leaves synthesis to the model. For a working reference, see `src/tools/sentiment/reddit-sentiment.ts`. ## Quick Start 1. Create your tool file: `src/tools//my-tool.ts` 2. Add a provider if needed: `src/providers/my-source.ts` 3. Add a type if needed: `src/types/my-domain.ts` 4. Register in `src/tools/index.ts` → `getAllTools()` 5. Add fixture JSON in `tests/fixtures//` 6. Write tests, run `npm test`, submit PR ## The Tool Contract Every tool is an `AgentTool` with Typebox parameters: ```ts import { Type } from "@sinclair/typebox"; import type { AgentTool } from "@earendil-works/pi-agent-core"; const params = Type.Object({ symbol: Type.String({ description: "Stock ticker symbol (e.g. AAPL)" }), days: Type.Optional(Type.Number({ description: "Lookback in days. Default: 7" })), }); export const twitterSentimentTool: AgentTool = { name: "get_twitter_sentiment", label: "Twitter Sentiment", description: "Analyze Twitter/X sentiment for a stock ticker", parameters: params, async execute(toolCallId, args) { // Fetch data via provider, format results return { content: [{ type: "text", text: "Formatted human-readable output" }], details: { sentiment: 0.72, volume: 1234 }, }; }, }; ``` ### Naming Rules - **snake_case** with a verb prefix: `get_`, `analyze_`, `search_`, `calculate_`, `compare_`, `compute_`, `track_`, `manage_`, `backtest_`, `list_`, `fetch_`, `check_` - You can use `createTool()` from `src/tool-kit.ts` to validate this at creation time (optional convenience) ### Parameters - Use Typebox `Type.Object({...})` as the root (recommended convention) - Every parameter needs a `description` — the agent reads these to decide how to call the tool - Use `Type.Optional()` for non-required params ### Return Format ```ts { content: [{ type: "text", text: string }], // Displayed to user details: T, // Structured data for agent } ``` - `content` — human-readable. Format nicely (tables, bullet points) - `details` — typed structured data the agent reasons over ## Where Files Go ``` src/tools//my-tool.ts # Tool implementation src/providers/my-source.ts # API client (if new data source) src/types/my-domain.ts # Types (if new domain) src/tools/index.ts # Register in getAllTools() tests/fixtures// # Fixture JSON for mock responses tests/unit/tools/my-tool.test.ts # Unit tests ``` ### Registering the Tool Add your export to `src/tools/index.ts`: ```ts import { twitterSentimentTool } from "./sentiment/twitter-sentiment.js"; export function getAllTools(): AgentTool[] { return [ // ... existing tools twitterSentimentTool, ]; } ``` That's it — the tool is now available to the agent. If the tool should be available in normal OpenCandle conversations, also wire it into active-tool selection: - Add the tool name to the relevant bundle in `src/routing/route-manifest.ts` (for example, core market tools belong in `TOOL_BUNDLE_TOOLS.core_market`). - Update the tool catalog text in `src/prompts/context-builder.ts` and, when the global prompt mentions the same domain, `src/system-prompt.ts`. - Add tests showing the tool is present for the intended bundle and absent from unrelated bundles. - Add prompt or harness coverage proving the agent chooses the new tool for the intended prompt class and keeps existing tools for adjacent tasks. `screen_stocks` is the reference for a provider-backed tool that needed this wiring: it is exposed for breadth/screening prompts, while Yahoo-backed quote/history tools remain the right choice for single-security quote or history prompts. ## Using OpenCandle Infrastructure ### HTTP Client ```ts import { httpGet, httpPost } from "../../infra/http-client.js"; const data = await httpGet("https://api.example.com/data", { headers: { Authorization: `Bearer ${apiKey}` }, }); const posted = await httpPost("https://api.example.com/search", { query: "AAPL", }); ``` `httpPost` is internal first-party infrastructure at the moment; add-on packages should continue using the currently exported `opencandle/tool-kit` APIs unless that package subpath explicitly exports new HTTP helpers. ### Caching ```ts import { cache, TTL } from "../../infra/cache.js"; const cached = cache.get("my-tool:AAPL"); if (cached) return cached; const fresh = await fetchData("AAPL"); cache.set("my-tool:AAPL", fresh, TTL.MINUTES_15); ``` For providers that may fail intermittently, use `cache.getStale()` to return the last known value within a longer window: ```ts import { STALE_LIMIT } from "../../infra/cache.js"; const stale = cache.getStale("my-tool:AAPL", STALE_LIMIT.SENTIMENT); if (stale) return stale.value; // serve stale data while provider is down ``` ### Rate Limiting ```ts import { rateLimiter } from "../../infra/rate-limiter.js"; await rateLimiter.acquire("my-api"); ``` Configure first-party provider buckets in `src/infra/rate-limiter.ts`, and use the provider ID consistently in tests and `wrapProvider()`. ### New Provider Checklist For a first-party provider, follow the Yahoo provider pattern in `src/providers/yahoo-finance.ts`: - Put the API client in `src/providers/.ts` with verb-prefixed async functions that return typed objects. - Use shared `httpGet`/`httpPost`, `rateLimiter.acquire("")`, `cache`, TTLs, and stale-cache fallback instead of provider-local fetch/retry logic. - Export the provider functions from `src/providers/index.ts`. - Save deterministic fixtures in `tests/fixtures//`; unit tests must mock `globalThis.fetch` and must not call live APIs. - Decode provider responses defensively and surface provider limits or freshness caveats in tool output. - If the provider backs a new tool, register the tool in `src/tools/index.ts`, route bundles, prompt catalog text, and focused tests. ### Provider Wrapping Use `wrapProvider()` for circuit-breaking and error handling: ```ts import { wrapProvider } from "../../providers/wrap-provider.js"; const result = await wrapProvider("my-source", () => fetchFromMyApi(symbol)); if (result.status === "unavailable") { return { content: [{ type: "text", text: `Data unavailable: ${result.reason}` }], details: null }; } // result.stale is true when serving cached data after a provider failure ``` For multi-provider fallback, use `withFallback()` — see `src/tools/market/stock-quote.ts`. For a real-world example that uses `wrapProvider` with stale fallback and login-specific error detection, see `src/tools/sentiment/twitter-sentiment.ts`. ## Testing Mock `globalThis.fetch` with fixture JSON. No live API calls in unit tests. ```ts import { describe, it, expect, vi, beforeEach } from "vitest"; const FIXTURE = { sentiment: 0.72, posts: 150 }; describe("get_twitter_sentiment", () => { beforeEach(() => { vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: true, json: () => Promise.resolve(FIXTURE), })); }); it("returns sentiment data for a valid ticker", async () => { const result = await twitterSentimentTool.execute("call-1", { symbol: "AAPL" }); expect(result.details.sentiment).toBe(0.72); expect(result.content[0].text).toContain("AAPL"); }); }); ``` Save fixture JSON in `tests/fixtures//` so tests are deterministic. ## Add-on Packages (Advanced) If your tool has heavy dependencies or needs separate maintenance, you can ship it as a standalone npm package instead. It's a Pi extension that imports from `opencandle/tool-kit`: ```ts // extension.ts in your separate package import type { ExtensionAPI } from "opencandle/tool-kit"; import { registerTools } from "opencandle/tool-kit"; import { myTool } from "./tools/my-tool.js"; export default function(pi: ExtensionAPI): void { registerTools(pi, [myTool]); } ``` ```json // package.json { "pi": { "extensions": ["./dist/extension.js"] }, "keywords": ["opencandle-tools"], "peerDependencies": { "opencandle": "*" } } ``` Pi discovers it automatically when installed. For Pi extension lifecycle details, see [Pi documentation](https://github.com/nicobailon/pi-coding-agent). ## Checklist - [ ] Tool name is snake_case with verb prefix - [ ] Every parameter has a `description` - [ ] `execute()` returns `{ content, details }` - [ ] Uses `cache` and `rateLimiter` for external API calls - [ ] Tests mock `globalThis.fetch` with fixtures - [ ] Tool registered in `src/tools/index.ts` ## Investigation Quality Checklist - [ ] The tool reports source/provider identity in structured details when useful - [ ] Missing credentials produce a clear setup path instead of a vague failure - [ ] Stale or partial data is labeled in the user-facing content - [ ] The tool avoids advice language such as "buy", "sell", or "safe" - [ ] Downside or data-quality caveats are preserved for the analyst prompt - [ ] Fixture data is realistic enough to catch formatting and parsing regressions --- ## Testing and Evals Source: https://opencandle.app/docs/testing-and-evals.html # Testing and Evals OpenCandle has four validation layers: - deterministic unit tests for pure logic, request-understanding fixtures, providers with mocked fetch, and GUI state helpers - focused end-to-end tests for CLI, credential flows, and live provider/tool behavior - browser smoke tests for the local GUI - evals that run full OpenCandle sessions through the shared agent harness and score product behavior, task selection, evidence use, or competitive performance ## Baseline Checks Run these before treating a checkout as healthy: ```bash npm test npm run gui:web:build npm run docs:site:build ``` `npm test` runs the Vitest suite. Unit tests should be fixture-backed and should not call live APIs. ## End-to-End Tool Tests ```bash npm run test:e2e npm run test:e2e:cli npm run test:e2e:credential-prompt npm run test:e2e:credential-snooze npm run test:e2e:credential-soft-fallback npm run test:e2e:credential-per-workflow-cap ``` `npm run test:e2e` intentionally hits live APIs through focused tool checks. The provider matrix is broader and also live: ```bash npm run test:e2e:providers ``` Only run these when live network/API behavior is part of the validation goal. ## Eval Commands OpenCandle separates deterministic tests from opt-in evals because evals may depend on model credentials, live data, local agent CLIs, or longer-running traces. ```bash npm run test:evals npm run test:evals:usually npm run eval:router-live npm run test:evals:product npm run test:evals:competitive ``` | Command | What it runs | When to use it | |---------|--------------|----------------| | `npm run test:evals` | Vitest eval cases under `tests/evals/cases/**/*.eval.ts` | Deterministic or semi-deterministic scoring cases that should run as a suite. | | `npm run test:evals:usually` | Same Vitest eval suite with `EVAL_TIER=usually` | The common eval tier when you want the usual subset rather than every case. | | `npm run eval:router-live` | `tests/scripts/run-live-router-eval.ts` against request-understanding fixtures with a live model | Opt-in task-selection quality check. Requires live model credentials and compares live output to fixture expectations. | | `npm run test:evals:product` | `tests/scripts/run-product-evals.ts` | Full-session product evals over curated finance prompts, using the OpenCandle harness and rubric-style dimensions. | | `npm run test:evals:competitive` | `tests/scripts/run-competitive-finance-eval.ts` | Competitive finance benchmark against generic no-tool Claude, Codex, and Gemini baselines. See [Benchmarking](./benchmarking.md). | Eval reports are written under `tests/evals/runs/` when a runner produces a JSON report. Treat those run files as local evidence, not committed documentation. ## Product Evals Product evals run curated prompts through `runOpenCandleSession()` and score the resulting trace for investigation fit, tool usage, directness, evidence use, risk framing, horizon fit, and honest handling of missing data. Prompt families currently include: - `single_asset` - `compare_assets` - `portfolio` - `options` - `sentiment` - `macro` - `education` Run all product evals: ```bash npm run test:evals:product ``` Useful environment variables: - `PRODUCT_EVAL_CASE`: run one case by id, such as `compare-assets-aapl-msft-6mo`. - `PRODUCT_EVAL_FAMILY`: run one family, such as `portfolio` or `macro`. - `PRODUCT_EVAL_LIMIT`: run only the first N selected cases. Example: ```bash PRODUCT_EVAL_FAMILY=options PRODUCT_EVAL_LIMIT=1 npm run test:evals:product ``` Each run writes a timestamped `*_product-evals.json` report under `tests/evals/runs/`. ## GUI Browser Smoke Run the GUI in one terminal: ```bash npm run gui ``` Then run the browser smoke in another terminal: ```bash npm run test:gui:browser ``` Set `OPENCANDLE_GUI_URL` to target a non-default local URL. GUI smoke testing should cover desktop and mobile widths when UI behavior changes. For visual or GUI behavior changes, also build the web bundle: ```bash npm --workspace @opencandle/gui-web run build ``` At minimum, exercise prompts that render stock quotes, quote comparison, options chains, SEC filings, macro/FRED data, and news/search so the matching tool cards and financial context panel render from saved session state. ## Agent Harness The file-based harness lets another coding agent drive OpenCandle as a simulated user and inspect the resulting trace. ```bash npx tsx tests/harness/cli.ts run --prompt "What is AAPL trading at?" --ipc /tmp/oc-test & npx tsx tests/harness/cli.ts wait --ipc /tmp/oc-test npx tsx tests/harness/cli.ts trace --ipc /tmp/oc-test ``` If the run asks a question: ```bash npx tsx tests/harness/cli.ts answer --ipc /tmp/oc-test --value "Moderate" ``` The final `trace.json` includes tool calls, results, interactions, final text, duration, and OpenCandle custom entries such as workflow dispatch, request-understanding output, disclaimers, and degradation notes. ## Request-Understanding Fixtures Request-understanding fixtures live in `tests/fixtures/router/` and are included in `npm test`. Use them when changing: - `src/routing/router-prompt.ts` - `src/routing/router.ts` - task-selection model choice - multi-turn context handling - preference extraction or slot resolution The live fixture eval is opt-in: ```bash npm run eval:router-live ``` It uses `OPENCANDLE_ROUTER_PROVIDER` and `OPENCANDLE_ROUTER_MODEL` when set. Defaults are `anthropic` and `claude-haiku-4-5`, so it requires matching live model credentials unless you override those env vars. Treat task-selection mismatches as regressions even when the aggregate pass rate looks acceptable. ## Test Data Rules - Mock `globalThis.fetch` in unit tests. - Store response fixtures under `tests/fixtures//`. - Do not commit real account balances, names, or exact holdings in fixtures. - Preserve classification-relevant signal such as tickers, horizons, and risk phrasing. - Keep live API checks out of the default unit test path. --- ## Benchmarking Source: https://opencandle.app/docs/benchmarking.html # Benchmarking OpenCandle benchmark runs are designed to answer a product question: when does a finance-native agent with market tools, guided workflows, and traceable evidence produce a more useful answer than a generic agent answering without tools? The benchmark is not meant to prove OpenCandle always wins. Generic agents can be stronger on concise education, broad explanations, or clean synthesis when live data is unnecessary. OpenCandle should shine when the user benefits from the right investigation path, fresh market evidence, explicit tool traces, risk framing, and honest disclosure of missing data. ## Competitive Finance Benchmark Before running this, expect live model/API usage, local baseline-agent requirements, and multi-minute runs. OpenCandle needs model credentials for its own run. The judge model uses `OPENCANDLE_COMPETITIVE_PROVIDER` and `OPENCANDLE_COMPETITIVE_MODEL` when set; otherwise it prefers configured Google auth with `gemini-2.5-flash`, then the first configured model. Claude, Codex, and Gemini baselines run through `acpx`; unavailable baselines are recorded as skipped unless `OPENCANDLE_COMPETITIVE_REQUIRE_ALL=1`. Run: ```bash npm run test:evals:competitive ``` The runner: 1. Generates or accepts finance prompts for the run date. 2. Runs each prompt through OpenCandle with the shared in-process harness. 3. Runs the same prompt through Claude, Codex, and Gemini as generic no-tool finance agents through `acpx`. 4. Uses a configured judge model to compare usefulness, correctness, evidence, clarity, and uncertainty handling. 5. Writes a timestamped `*_competitive-finance.json` report under `tests/evals/runs/`. The committed docs should describe durable benchmark design and public summaries. Do not commit raw internal transcripts or one-off run reports. ## Useful Commands Run the default generated prompt set: ```bash npm run test:evals:competitive ``` Run a small generated set: ```bash COMPETITIVE_PROMPT_COUNT=1 npm run test:evals:competitive ``` Rerun a fixed prompt after a product or harness change: ```bash OPENCANDLE_COMPETITIVE_PROMPT_ID=fixed-rates-growth \ OPENCANDLE_COMPETITIVE_PROMPT_TOPIC=macro \ OPENCANDLE_COMPETITIVE_PROMPT_COMPLEXITY=complex \ OPENCANDLE_COMPETITIVE_PROMPT="How should falling rates affect growth stocks over the next year?" \ npm run test:evals:competitive ``` ## Configuration Prompt selection: - `COMPETITIVE_PROMPT_COUNT`: number of generated prompts. Defaults to `5`. - `COMPETITIVE_PROMPT_SEED`: seed text for reproducible generation. Defaults to the current date. - `OPENCANDLE_COMPETITIVE_PROMPT`: fixed user prompt. When set, prompt generation is skipped. - `OPENCANDLE_COMPETITIVE_PROMPT_ID`: id for a fixed prompt. Defaults to `fixed-prompt`. - `OPENCANDLE_COMPETITIVE_PROMPT_TOPIC`: topic for a fixed prompt. Defaults to `fixed prompt`. - `OPENCANDLE_COMPETITIVE_PROMPT_COMPLEXITY`: `simple`, `moderate`, or `complex`. Defaults to `moderate`. - `OPENCANDLE_COMPETITIVE_PROMPT_FOCUS`: optional judge focus for the fixed prompt. Judge model: - `OPENCANDLE_COMPETITIVE_PROVIDER`: provider for prompt generation and judging. - `OPENCANDLE_COMPETITIVE_MODEL`: model id for prompt generation and judging. Generic-agent baselines: - `OPENCANDLE_COMPETITIVE_ACPX_COMMAND`: override the `acpx` command. Defaults to the repo-local binary. - `OPENCANDLE_COMPETITIVE_CLAUDE_AGENT_COMMAND`: override the Claude ACP adapter command. - `OPENCANDLE_COMPETITIVE_CODEX_AGENT_COMMAND`: override the Codex ACP adapter command. - `OPENCANDLE_COMPETITIVE_GEMINI_AGENT_COMMAND`: override the Gemini ACP adapter command. - `OPENCANDLE_COMPETITIVE_CODEX_MODEL`: Codex baseline model. Defaults to `gpt-5.3-codex-spark[medium]`. - `OPENCANDLE_COMPETITIVE_AGENT_CWD`: isolated working directory for baseline agents. Defaults to a temp directory. - `OPENCANDLE_COMPETITIVE_AGENT_TIMEOUT_SECONDS`: `acpx` timeout for each baseline call. Defaults to `900`. - `OPENCANDLE_COMPETITIVE_AGENT_TIMEOUT_MS`: process timeout for each baseline call. Defaults to `900000`. - `OPENCANDLE_COMPETITIVE_PREFLIGHT`: set to `0` to skip one-time baseline smoke calls. - `OPENCANDLE_COMPETITIVE_REQUIRE_ALL`: set to `1` to fail if any baseline is unavailable. By default, unavailable baselines are recorded as skipped and the run continues. - `OPENCANDLE_MANUAL_RUN_SETTLE_GRACE_MS`: settle window for OpenCandle traces. Defaults to `30000` in this runner. Developer diagnostic: - `OPENCANDLE_ROUTER_MODE`: advanced request-understanding mode. Keep the default unless you are intentionally comparing task-selection behavior. ## Where OpenCandle Should Shine OpenCandle should outperform generic no-tool agents when the answer depends on: - current quote, options, technical, sentiment, filing, macro, or crypto data - choosing the right investigation path before answering - combining several evidence types into one investment decision - preserving a trace of tool calls, workflow dispatch, disclaimers, and degradation notes - asking for missing risk tolerance, horizon, budget, or objective only when that information changes the answer - naming downside scenarios and uncertainty instead of presenting unsupported conviction Generic agents may still win when a prompt is purely educational, needs no current data, or rewards a shorter explanation over trace-backed evidence. Those outcomes are useful benchmark signal: they show where OpenCandle should reduce workflow ceremony, improve synthesis, or avoid fetching data that does not change the answer. --- ## Comparisons Source: https://opencandle.app/docs/comparisons.html # Comparisons OpenCandle is best compared with the tools people already use for market research: a general chatbot, a browser full of finance sites, a spreadsheet, or custom scripts. OpenCandle is different because it gathers provider-backed evidence through explicit finance tools, preserves provider gaps, and then asks the model to synthesize from the evidence trail. OpenCandle is read-only research software. It does not place trades, route orders, or provide financial advice. ## OpenCandle vs ChatGPT ChatGPT is a general assistant. It can explain concepts and help structure research, but it does not automatically call OpenCandle's finance tools, track local portfolio state, or show a provider-by-provider evidence trail unless you build that workflow yourself. OpenCandle uses model providers for synthesis, but the finance workflow starts with explicit tools for quotes, options, filings, macro data, sentiment, fundamentals, crypto data, and portfolio context. This makes it better suited to questions where the user needs to know which data source was used, whether the source was stale, and which risks were visible before the final answer was written. Read the focused comparison: [OpenCandle vs ChatGPT](./opencandle-vs-chatgpt.md). ## OpenCandle vs Finance Websites Finance websites are strong for single-source lookup: a quote page, a filing page, a chart, or a news feed. They become slower when a question needs evidence from several domains at once. OpenCandle is useful when the research path crosses sources. A single investigation can combine a quote snapshot, SEC EDGAR filings, FRED macro series, option chain data, Reddit or web sentiment, and local portfolio state. The answer can then cite the gathered evidence and call out gaps instead of leaving the user to reconcile tabs manually. ## OpenCandle vs Spreadsheets Spreadsheets are excellent for owned models, repeatable calculations, and portfolio tracking. They are less ergonomic for conversational investigation, provider degradation, and ad hoc evidence gathering. OpenCandle complements spreadsheets by handling the research loop before a model is built: identify the question, gather current evidence, surface missing inputs, and produce a risk-aware summary. When a spreadsheet is the right final artifact, OpenCandle can still be the first pass for finding the relevant facts and caveats. Read the focused comparison: [OpenCandle vs Spreadsheets](./opencandle-vs-spreadsheets.md). ## OpenCandle vs Custom Scripts Custom scripts are ideal when a team has a stable workflow, a known provider, and a fixed output shape. They can be brittle when the question changes or the provider returns partial data. OpenCandle keeps the script-like benefits of explicit tools while adding a conversational routing layer. It can choose a quote lookup, filing search, options read, macro pull, sentiment summary, or portfolio review based on the prompt, then record degraded sources in the same session. ## When OpenCandle Is Not The Right Tool Use something else when you need order routing, regulated financial advice, high-frequency trading infrastructure, proprietary terminal data, or fully automated investment decisions. OpenCandle is built for research and evidence inspection, not execution. Use a spreadsheet or notebook when the primary task is a model you already understand and need to maintain over time. Use a finance terminal or paid data feed when the binding requirement is proprietary data coverage, exchange licensing, or institutional workflow integration. --- ## OpenCandle vs ChatGPT Source: https://opencandle.app/docs/opencandle-vs-chatgpt.html # OpenCandle vs ChatGPT OpenCandle and ChatGPT solve different parts of a market research workflow. ChatGPT is a general assistant that can explain concepts, organize a thesis, and help write analysis. OpenCandle is a finance-specific agent that gathers explicit evidence first: quotes, price history, options chains, SEC filings, macro data, sentiment, fundamentals, crypto data, and local portfolio context. OpenCandle is read-only research software. It does not place trades, route orders, or provide financial advice. ## The Short Version Use ChatGPT when the task is mostly writing, education, brainstorming, or turning already-collected facts into a cleaner explanation. Use OpenCandle when the task depends on current or inspectable financial evidence and you want the answer to show provider gaps, stale data warnings, and tool output before the model writes. ## Comparison Table | Capability | OpenCandle | ChatGPT | | --- | --- | --- | | Finance tools | Built-in tool calls for market, macro, options, filings, sentiment, fundamentals, crypto, and portfolio workflows | Depends on the ChatGPT plan, connectors, browsing mode, or custom GPT setup | | Local GUI | Yes, with chat, tool cards, provider status, sessions, and financial context | No OpenCandle-specific GUI | | Terminal agent | Yes, through `npx opencandle@latest` | No native OpenCandle terminal workflow | | Provider gaps | Preserved in the session and answer context | Usually manual unless a custom workflow records them | | Local portfolio state | Supported through OpenCandle state | Requires a separate file, spreadsheet, connector, or custom setup | | Order routing | Not supported | Not supported | ## Why Evidence First Matters A finance answer can sound confident while resting on stale, missing, or mismatched data. OpenCandle is designed to make that failure mode visible. A quote answer can show the provider and timestamp; a filing answer can point to SEC EDGAR results; an options answer can preserve per-share versus per-contract context; a macro answer can name the FRED series; and a portfolio answer can separate saved local holdings from new user input. ChatGPT can help interpret those facts, but OpenCandle gives the research loop a finance-specific evidence layer before interpretation starts. ## When ChatGPT Is Enough ChatGPT is often enough for conceptual education, general finance definitions, writing help, or analysis where the user already has the relevant numbers. If the question is "explain duration risk" or "rewrite this investment memo", a general assistant can be the right tool. ## When OpenCandle Is Better OpenCandle is better when the question asks for current market evidence, a visible source trail, local portfolio context, or repeatable workflows. Examples include "What is AAPL trading at?", "Compare MSFT and GOOGL using fundamentals and sentiment", "Show TSLA puts with Greeks", "Get CPI from FRED", and "Review my NVDA position with downside risks." In those cases, the important difference is not the prose style; it is whether the system gathered the right data before writing. --- ## OpenCandle vs Spreadsheets Source: https://opencandle.app/docs/opencandle-vs-spreadsheets.html # OpenCandle vs Spreadsheets OpenCandle and spreadsheets work best at different stages of financial research. Spreadsheets are strong for owned models, repeatable calculations, scenario tables, and long-lived portfolio tracking. OpenCandle is stronger at the front of the workflow: gathering current evidence, routing a question to the right finance tools, recording provider gaps, and turning the evidence trail into a risk-aware answer. OpenCandle is read-only research software. It does not place trades, route orders, or provide financial advice. ## The Short Version Use a spreadsheet when you already know the model you want to maintain. Use OpenCandle when you are still gathering evidence across quotes, filings, macro data, options chains, sentiment, fundamentals, crypto data, or portfolio context and you want the tool output visible before synthesis. ## Comparison Table | Capability | OpenCandle | Spreadsheet | | --- | --- | --- | | Evidence gathering | Agent routes prompts to explicit finance tools | Manual imports, formulas, plugins, or pasted data | | Provider gaps | Missing keys, stale data, and degraded sources are surfaced in the session | Usually tracked manually in notes or formulas | | Portfolio context | Local state can be referenced in a conversation | Strong for maintained positions, allocation tables, and formulas | | Repeatable models | Useful for first-pass research and workflow routing | Best fit for durable valuation, allocation, and scenario models | | Collaboration artifact | Chat transcript, tool output, docs, and markdown context | Workbook tabs, formulas, comments, and exports | | Trade execution | Not supported | Not supported unless connected to separate brokerage tooling | ## Where OpenCandle Helps First OpenCandle helps when the research question is still fluid. A user can ask for a quote, compare two assets, inspect options, pull a FRED macro series, search SEC filings, summarize sentiment, or review a local portfolio position without building a workbook first. The resulting answer can show which provider supplied the evidence, where data was missing, and what risks matter before any spreadsheet model is worth maintaining. ## Where Spreadsheets Still Win Spreadsheets win when the workflow is stable and arithmetic-heavy. Discounted cash flow models, position sizing tables, tax lots, rebalancing plans, and scenario matrices belong in a workbook when the user needs complete control over formulas and assumptions. ## How To Use Them Together Use OpenCandle to gather the facts and caveats, then move stable assumptions into a spreadsheet. This pairing keeps the spreadsheet focused on the model while OpenCandle handles the messy first pass across sources, provider availability, and natural-language follow-up questions. --- ## Contributing to OpenCandle Source: https://opencandle.app/docs/contributing.html # Contributing to OpenCandle OpenCandle is a financial data analysis agent built with TypeScript, Vitest, and Pi. Contributions should keep the runtime small, the data flow explicit, and the quality bar high enough for a public npm package. ## Before You Start - For non-trivial features, start with an issue or discussion before opening a PR. - Bug fixes should include a clear reproduction in the PR description. - Behavior-changing work must include tests. - Keep user-facing claims factual. Do not document or imply package behavior that does not exist yet. ## Local Setup ```bash npm install cp .env.example .env npm start ``` If you need provider keys for manual testing, prefer `.env` for local work. Unit tests must continue to run without live API access. ## Development Commands ```bash npm start npm test npm run test:watch npm run test:e2e npm run test:e2e:cli npm run test:e2e:providers ``` `npm test` is the required baseline validation after changes. ## Contribution Rules ### TDD is mandatory Write or update the failing test first, then implement the change. This is not optional for runtime behavior. If a change affects behavior and has no test coverage, it is incomplete. ### Keep tool boundaries clean - Tools fetch and format data - Analysts and prompts synthesize - Do not move analysis logic into tools ### Keep provider tests fixture-based - Unit tests must mock `globalThis.fetch` - Do not make live API calls in unit tests - Add fixture JSON under `tests/fixtures//` for new provider responses ### Keep typing strict - Avoid `any` except for raw provider payloads at the API boundary - Use `.js` extensions on relative imports - Use `node:` prefixes for built-in modules ## Pull Requests Open focused PRs with enough context for review. Every PR should explain: - what changed - why it changed - user or maintainer impact - test coverage added or updated - risks, follow-ups, or known gaps For non-trivial work, link the issue or design discussion that established scope. ## Release Notes and Changelog Discipline OpenCandle follows Pi's release style where possible: manual semver bump scripts, a maintained `CHANGELOG.md` with an `Unreleased` section, and explicit release commands. Prefer clear prefixes such as: - `feat:` - `fix:` - `docs:` - `refactor:` - `test:` Release notes should describe user-visible impact, not just implementation detail. Release commands: ```bash npm run version:patch npm run version:minor npm run version:major npm run publish:dry npm run release:patch npm run release:minor npm run release:major ``` The `release:*` scripts are intended for maintainers. They bump the version, update `CHANGELOG.md`, create a release commit and tag, restore the `Unreleased` section for the next cycle, and push both `main` and the release tag. The actual npm publish step runs in GitHub Actions from the pushed `v*` tag using trusted publishing. That keeps the local release flow minimal while avoiding laptop-based npm publishes. ## Scope Boundaries Ask first before changing: - system prompt or analyst orchestration - Pi shell integration under `src/pi/` - memory SQLite schema - provider strategy that needs new rate-limit or fixture policy Do not: - guess financial numbers or metrics - hardcode mock data into tools - make live API calls in unit tests - blur the separation between Pi-owned config and OpenCandle-owned state ## Where Things Live - Providers: `src/providers/` - Tools: `src/tools/` - Request understanding: `src/routing/` - Workflows: `src/workflows/` - Memory: `src/memory/` - Pi integration: `src/pi/` - Tests and fixtures: `tests/` The repo-level [AGENTS.md](./AGENTS.md) remains the most specific implementation guide for code changes. --- ## Security Policy Source: https://opencandle.app/docs/security.html # Security Policy ## Supported Versions Until OpenCandle reaches `1.0.0`, security support is limited to the latest pre-1.0 release line. | Version | Supported | | ------- | --------- | | Latest `0.x` release | Yes | | Older pre-1.0 releases | No | | Unreleased local forks | No | ## Reporting a Vulnerability Do not open public GitHub issues for security vulnerabilities. Use GitHub Security Advisories or the repository's private vulnerability reporting channel to report suspected vulnerabilities to the maintainers. When reporting an issue, include: - affected version or commit - impact summary - reproduction steps or proof of concept - any suggested mitigation if known Maintainers will acknowledge valid reports, investigate them privately, and coordinate a fix and release before public disclosure when practical.