Skip to content

Configuration

Abid Ali Awan edited this page Oct 14, 2026 · 18 revisions

Only need keys? Providers has the sign-up links and tells you which ones actually matter.

Run uv run setup to configure interactively, or copy .env.example to .env and override only what you need. Every setting is optional; without an LLM key, the app starts in mock mode. The groups below mirror the sections of .env.example, the defaults live in app/config.py, and everything here is also editable in the app's Settings page without a restart.

The Settings page

Everything on this page is also editable in the app: launch it and open Settings (the /settings page). How the two layers work:

  • .env values are the defaults; a value saved in the app becomes an override stored in the SQLite database and applies immediately (no restart).
  • API keys saved in the app never touch the database: they go to the OS credential vault via keyring (Windows Credential Manager, macOS Keychain, Linux Secret Service) and override .env keys. The API never returns a secret value, only a "configured" flag and where the secret is stored.
  • Reset to .env defaults drops every override and deletes the keychain entries. Without an OS keychain (some headless Linux setups), secrets fall back to the local database unencrypted and the Settings page warns about it.
  • The sidebar dots show which sections are ready: green = configured, hollow = optional and unset, amber = a caveat (for example the Alpaca kill switch is off), red = missing (no LLM key).
  • Keychain keys do not travel with a copied project folder; re-enter them on a new machine.

.env stays gitignored, and uv run setup remains the fastest first-run path.

The setup wizard

uv run setup (or python scripts/setup_wizard.py before dependencies are installed) walks you through everything:

  • Provider presets with verified base URLs: OpenAI, Z.AI (GLM), DeepSeek, Alibaba Cloud Qwen (DashScope), OpenRouter, or any custom OpenAI-compatible endpoint.
  • Your API key is hidden while typing, and can be validated with a one-token request (a 401 reads as "key rejected", a 404 as "check base URL or model").
  • Optional market-data keys, each with its fallback explained.
  • Existing .env values are preserved; Ctrl+C at any prompt writes nothing.
  • Colors on terminals (plain when piped or NO_COLOR is set; BTA_WIZARD_COLOR=1 forces them on).

Recommended models

Two jobs, two models: a fast one for the researchers and debaters, a stronger one for the manager's call. Sign-up links live on Providers.

For fast agent work, we recommend one of:

Provider LLM_MODEL value Why choose it
OpenAI gpt-6-luna Fast agent work at list prices that stay cheap per run
Z.AI glm-5.3-flash Efficient reasoning with strong quality at a lower serving cost
DeepSeek deepseek-4.1-flash Cost-efficient general reasoning for multi-agent runs

For reasoning and the report (the manager override), we recommend one of:

Provider LLM_MODEL_MANAGER value Why choose it
OpenAI gpt-6.1-sol Deeper reasoning for the final BUY/HOLD/SELL call
Z.AI glm-5.3 Strong judgment without premium pricing
MiniMax minimax-v2.6-pro Strong long-form analysis for the decision report

Set the fast one as LLM_MODEL and the strong one as LLM_MODEL_MANAGER (see Per-role models below). Model ids shift over time: copy the exact id from your provider's console. Pricing and availability vary by region.

Per-role models

Set LLM_MODEL to the cheap fast model, then override the manager so the final judgment runs on the stronger one; the researchers and debaters stay fast and inexpensive while the BUY/HOLD/SELL call gets the deep model. Each role can also point at its own endpoint and key:

LLM_MODEL=zai-org/GLM-5.3-Flash
LLM_MODEL_MANAGER=zai-org/GLM-5.3
LLM_BASE_URL_MANAGER=https://your-glm-53-endpoint/v1

LLM

Any OpenAI-compatible endpoint.

Variable Default Purpose
LLM_BASE_URL https://api.openai.com/v1 OpenAI-compatible API endpoint
LLM_API_KEY Not set Enables the LLM agents (researchers, debaters, manager)
LLM_MODEL gpt-5.6-luna Model used by every agent without a per-role override
LLM_TEMPERATURE 0.2 Sampling temperature
LLM_TIMEOUT_SECONDS 90 Timeout for each agent call
LLM_REASONING_EFFORT Not set Optional provider-specific reasoning effort (e.g. none/low for GLM)
LLM_PRICE_IN / LLM_PRICE_OUT 0 Cost-estimate override in USD per 1M input/output tokens for every role (custom/proxied pricing); 0 uses the built-in list-price table in app/cost.py
LLM_MODEL_MANAGER LLM_MODEL Per-role model for the final BUY/HOLD/SELL call
LLM_BASE_URL_MANAGER / LLM_API_KEY_MANAGER global values Optional endpoint/key just for the manager
LLM_MODEL_ANALYSTS (+ _BASE_URL_ / _API_KEY_) global values Per-role overrides for the 5 researchers
LLM_MODEL_DEBATE (+ _BASE_URL_ / _API_KEY_) global values Per-role overrides for the bull/bear debaters

Market-data providers

All optional; each has a built-in fallback.

Variable Default Purpose
FINNHUB_API_KEY Not set Company profiles, fundamentals, and news; falls back to yfinance
OLOSTEP_API_KEY Not set News search/scraping fallback and Reddit/StockTwits sentiment search
NIXTLA_API_KEY Not set Nixtla TimeGPT 5-day forecast; falls back to the next hosted provider
TSFM_API_KEY Not set TSFM.ai hosted Chronos-2 5-day forecast, used when TimeGPT is unavailable; falls back to the local trend model
SEC_EDGAR_USER_AGENT Not set Contact string sent to SEC EDGAR when replaying as-filed fundamentals in backtests; a project default is used unless set

Analysis

Variable Default Purpose
MAX_TICKERS 5 Maximum tickers accepted in one analysis
DEBATE_ROUNDS 2 Debate gate: 2+ enables the judge round at Max depth (single round below it; Fast runs no debate at all)
RUN_TIMEOUT_MINUTES 30 Wall-clock deadline for one analysis run; a hung provider call fails the run instead of leaving it running forever (P2.8)
STREAM_REASONING 0 Live reasoning stream: 1 streams agent tokens to the UI (off by default: the stream is mostly the final JSON and reads as noise)
EXPERIMENTAL_FEATURES 0 Master switch for the Settings Experimentation tab (1 shows it)
FEATURE_ACCURACY 0 1 serves the experimental Accuracy page (needs the master switch too)
FEATURE_COMPARE 0 1 serves the experimental Compare page (needs the master switch too)
FEATURE_WATCHLIST 0 1 serves the experimental Watchlist page (needs the master switch too)

Storage

Variable Default Purpose
DB_PATH portfolio.db SQLite app database path for run history and the paper order lifecycle

Holdings live in the Alpaca paper account, not in the app: there is no local portfolio ledger. STARTING_CASH and DEFAULT_POSITION_SIZE were removed with the demo portfolio; the paper account's own balance is the starting cash.

Alpaca paper trading

Optional. The feature is dormant until both key variables exist. Keys come from the paper section of the Alpaca dashboard, stay server-side, and are never returned by an endpoint or logged. The adapter is paper-only by construction: the SDK client is built with paper=True hardcoded and no base-url override exists.

Variable Default Purpose
ALPACA_API_KEY_ID empty Paper account key id (ALPACA_API_KEY accepted as an alias)
ALPACA_API_SECRET_KEY empty Paper account secret (ALPACA_SECRET_KEY accepted as an alias)
ALPACA_TRADING_ENABLED 0 Kill switch: 0 = read-only, order submissions refused
ALPACA_MAX_ORDER_USD 10000 Per-order notional cap
ALPACA_MAX_ORDERS_PER_DAY 20 Non-rejected submissions per UTC day
ALPACA_MAX_DECISION_AGE_HOURS 72 Research older than this cannot be traded
BROKER_SYNC_INTERVAL_MINUTES 15 Interval of the reconciliation sweep (minimum 5): refreshes non-terminal local orders by client order id and diffs Alpaca's open orders against them for the out-of-sync banner; a startup sweep runs before autopilot starts

Autopilot sessions

Automated paper-trading sessions scan the market, add held tickers, run the full pipeline, and submit risk-gated trades on a schedule. Each session always re-checks every held position (sell-or-hold) first, then fills the rest of the session from the market scan, skipping names it analyzed in the last few days so the shortlist rotates instead of repeating for weeks. The on/off toggle lives in the database (Trades page and Settings page) and is seeded from AUTOMATION_ENABLED on first boot. Full behavior in Usage.

Variable Default Purpose
AUTOMATION_ENABLED 0 Seeds the persisted toggle on first boot
AUTOMATION_INTERVAL_MINUTES 240 Minutes between sessions (minimum 15)
AUTOMATION_CANDIDATES 3 Discovered candidates per session (the "I Am Feeling Lucky" screen)
AUTOMATION_CANDIDATE_COOLDOWN_DAYS 3 Names analyzed in the last N days rest, so the shortlist rotates; 0 disables (held positions are always checked)
AUTOMATION_MIN_CONFIDENCE 0.65 BUYs need at least this final (risk-gated) confidence; SELLs always act
AUTOMATION_OUTLOOK short_term Session outlook: day_trade, short_term, or long_term
AUTOMATION_DEPTH pro Session depth: fast, pro, or max (legacy names medium/expert still accepted)
AUTOMATION_ALLOW_SELLS 1 A SELL decision exits the held position
AUTOMATION_SESSION_TIMEOUT_MINUTES 40 A session longer than this is cancelled and recorded as failed

Risk gate

Fractions of total equity applied to every BUY. The last row is a warning-only correlated-exposure brake (roadmap P1.3): it flags a BUY whose group exposure breaches the cap, it never blocks the trade.

Variable Default Purpose
MAX_POSITION_PCT 0.10 Max fraction of equity in one ticker
MAX_INVESTED_PCT 0.60 Max fraction of equity invested
MIN_CASH_PCT 0.10 Min cash buffer after a BUY
MAX_CORRELATED_PCT 0.25 Cap on the BUY ticker plus holdings whose daily returns correlate at/above 0.7 (app/risk.py); past it the result carries a concentration warning with before/after exposure. Missing price history reports unknown instead of a clean pass

Decision memory and calibration

Variable Default Purpose
MEMORY_HORIZON_DAYS 21 Fallback grading window in days; new runs grade on their outlook horizon (1 day day-trade, 5 short term, 21 long term)
MEMORY_SETTLE_INTERVAL_MINUTES 60 Interval of the scheduled settle pass (minimum 15) that grades every decision whose horizon window closed, even if its ticker is never analyzed again (P1.17)
MEMORY_REFLECT_WITH_LLM 0 1 asks the LLM for reflection lessons instead of deterministic sentences
CALIBRATION_MIN_OBSERVATIONS 30 Minimum mature graded decisions before a confidence bucket shows a track record instead of Track record unavailable

Backtests

Variable Default Purpose
BACKTEST_CACHE backtests/cache.db SQLite snapshot cache location (gitignored)
BACKTEST_OFFLINE 0 1 makes cache misses fail instead of hitting the network (proves warm re-runs are truly offline)

Clone this wiki locally