Roadmap - kingabzpro/BetterTradingAgents GitHub Wiki

Where the project is and where it's going. Last updated: 2026-10-14. The guiding belief: the app already has enough agents; the work that matters makes existing analysis easier to judge, easier to control, and easier to evaluate before adding execution.

Product rules

  • Evidence before persuasion. Show freshness, missing inputs, disagreement, and risk adjustments beside every call.
  • Keep confidence honest. It means evidence strength until historical results demonstrate calibration; it is not a probability of profit.
  • Deterministic code owns money and limits. LLMs summarize evidence; the risk gate sizes and gates positions.
  • Paper first, live never by accident. A recommendation and an order are separate user actions with a review step between them.
  • Keep the current stack. FastAPI, SQLite, vanilla HTML/CSS/JS, and the installed dependencies cover the planned work.

Status at a glance

Item Outcome Status
P0.1 Decision brief users can audit in seconds Done (2026-09-08)
P0.2 Cancel, rerun, and recover without guessing Done (2026-09-09)
P0.3 Accessible, mobile-safe core journey Done (2026-09-09)
P1.1 Historical confidence calibration Done (2026-09-09)
P1.2 Honest experiment and backtest workflow Done (2026-09-09)
P1.3 Portfolio-level concentration risk Done (2026-09-23)
P1.4 Watchlist and decision-change workflow Shipped 2026-09-25; removed by P1.13
P1.5 Compare decisions with price context Shipped 2026-09-25; removed by P1.13
P1.6 Find, filter, print, and export research Done (2026-09-27)
P1.7 Market-regime analyst (S&P 500, Nasdaq, VIX, 10-year yield) Done (2026-10-02, shipped as an analyst instead of a deterministic block)
P1.10 Debate judge replaces the rebuttal round Done (2026-10-02)
P1.11 Depth tiers rebalanced: Fast 3 / Pro 7 / Max 10, debate skipped at Fast Done (2026-10-02)
P1.12 Analysis UI modernization: verdict pills, confidence bars, debate strength bar, collapsible sections Done (2026-10-02)
P1.13 Remove Watchlist and Compare: no added user value Done (2026-10-03); both restored as opt-in experiments (Settings: Experimentation)
P1.14 TradingView analysis as optional research evidence Future (feasibility review first)
P1.8 Drop the CrewAI layer Deferred (keeping CrewAI)
P1.9 Accuracy report: past calls vs realized 21-day performance Done (2026-10-03; scripts/accuracy.py is the report, the page is an opt-in experiment)
P2.1 Direct paper-trading result loop Done (2026-09-29)
P2.2 Deployment and operational hardening Planned
P2.3 Broker sync and failure hardening Done (2026-10-14)
P2.4 Auto-exit rules, bear-market brake, webhooks Planned (base landed)
P2.5 In-app settings with OS-keychain secrets Done (2026-10-01)
P2.6 Browser notifications for finished sessions Done (2026-10-01)
P2.7 Guided first-run onboarding Planned
P2.8 Concurrency and run-lifecycle hardening Done (2026-10-14)
P2.9 Decision-truth and grading fixes Done (2026-10-03)
P2.10 Performance pass: history decode, caches, SQLite Planned (2026-10-03 audit)
P2.11 Local-safety hardening (first slice of P2.2) Planned (2026-10-03 audit)
P2.12 Simplify pass: dead code and duplication Planned (2026-10-03 audit)
P2.13 Drawdown brake memory: equity high-water mark Planned (2026-10-03)
P2.14 Run speed: overlap the data phase, budget provider timeouts, time every agent Done (2026-10-14)
P1.15 Point-in-time fundamentals from SEC EDGAR (as filed) Done (2026-10-12)
P1.16 Regional benchmarks and multi-market tickers Planned (2026-10-12, from the TradingAgents comparison)
P1.17 Settle every due decision, not only the analyzed ticker Done (2026-10-14)
P2.15 CI gate: offline checks and lint on every push Planned (2026-10-12, from the TradingAgents comparison)
P2.16 Data breadth: FRED macro and insider transactions Planned (2026-10-12, from the TradingAgents comparison)
P2.17 Resume an interrupted run from the last completed agent Planned (2026-10-12, from the TradingAgents comparison)
P2.18 Bound runaway reasoning: max_tokens and a retry budget Planned (2026-10-12, from the TradingAgents comparison)
P2.19 Local Chronos-2 forecast provider: drop the metered TimeGPT dependency Dormant (2026-10-13, owner call: no local forecasting in the app)
- Incognito mode: market-only runs with no portfolio, no saving, no trading Done (2026-10-04, user-requested off-roadmap)
- UI pass: one shared UI layer, decision-first results, real charts Done (2026-10-12, user-requested off-roadmap)
- Autopilot candidate rotation: recent picks rest, held positions always lead the session Done (2026-10-14, user-requested off-roadmap)

What shipped, in one breath

Parallel research (six analysts including market regime), a bull/bear debate that a neutral judge cross-examines at Max depth, and a risk-gated BUY/HOLD/SELL call with the full reasoning trail; every call graded against what the market actually did; walk-forward backtests with holdout honesty and as-filed SEC EDGAR fundamentals; search and export; Alpaca paper trading with scheduled autopilot sessions; in-app settings with OS-keychain secrets, applied live; browser notifications for finished sessions; incognito mode that runs the whole pipeline market-only and saves nothing; and a three-part UI pass: one shared UI layer with a drift-guard check suite, a decision-first results experience (verdict, evidence strength, and risk adjustments read at a glance), and real charts for price context, equity versus SPY, and the graded-call record. A hardening pass (2026-10-14) added broker reconciliation sweeps that settle crashed submissions and flag drift with an out-of-sync banner, serialized order guards with a wall-clock deadline on every run, and a scheduled settle pass that matures every due decision whether or not its ticker is analyzed again. A user-requested pass the same day gave autopilot sessions a rotating candidate shortlist (recently analyzed names rest) and put held positions first, so the session cap can never drop an open position's sell-or-hold check.

Next up

P1.14 TradingView analysis (future)

Explore adding TradingView technical analysis to each ticker's research: indicator summaries, timeframe-specific signals, and chart context, with the source, timestamp, and timeframe visible beside the evidence. Feed supported signals into the existing technical analyst and manager brief, and show agreement or disagreement with the app's own analysis.

First verify supported integration options, data availability, licensing, and access requirements; select the smallest viable integration after that review. Keep it optional, retain the current technical analysis when unavailable, and evaluate whether it improves graded outcomes before expanding it. This is a future research item, not an implemented integration.

P2.2 Deployment and operational hardening

The shared-deployment gate: migrations with tested rollback, owner isolation, backups, and operational metrics. Nothing ships shared before this is done.

P2.3 Broker sync and failure hardening (done 2026-10-14)

Shipped as one reconciliation pass. A startup sweep plus an interval sweep (BROKER_SYNC_INTERVAL_MINUTES, default 15) refresh every non-terminal local order against Alpaca by client order id: rows a crash left pending_submit or unknown settle honestly (filled, or rejected when Alpaca never saw them), and partial fills land in the row with quantity and price. The startup sweep runs before the automation loop starts, so recovery always precedes any new submission. Each sweep also diffs Alpaca's own open orders against the local set: an order this app did not place is stored as drift and shown as an out-of-sync banner on the portfolio page; GET /api/broker/sync refreshes a stored state staler than a minute, so polling tabs reach the broker at most once a minute. Broker read paths (account, positions, clock, equity, open orders) retry rate limits, broker 5xx, and network drops with a short backoff while auth and validation errors fail immediately; the order POST path still retries nothing blindly. Verified by the new check_broker_sync group (startup recovery, partial fills, drift and re-sync, sweep failure recorded, retry classification, endpoint staleness) and the whole suite including the browser smoke.

P2.4 Autopilot expansion: auto-exit rules, bear-market brake, webhooks

  • Auto-exit rules (no LLM): a price-only check evaluates every open position against stop-loss, take-profit, and max-hold rules, submitting exits through a guarded broker sibling; one exit attempt per position and reason per day.
  • Bear-market brake: when SPY sits below its 200-day average, sessions still research and still exit, but BUY submissions are recorded as braked.
  • Webhook notifications: one plain-text POST per finished session for users who want delivery without a browser tab (the browser-notification slice shipped in P2.6).

P2.7 Guided first-run onboarding

Detect unconfigured providers from the settings snapshot, walk through key entry with live validation (a one-token LLM ping, an Alpaca account read), and end with a preflight checklist mirroring the autopilot gate ladder. Skippable, and built entirely on the P2.5 settings store.

P2.8 Concurrency and run-lifecycle hardening (done 2026-10-14)

Shipped as one pass over the 2026-10-03 audit findings. Autopilot sessions take a real asyncio.Lock before any network call (the old "already running" boolean was set after three broker and discovery calls, so a schedule tick racing a manual run could trade at once), submit_order holds a lock across the open-order dedupe and daily-cap checks through the row insert (concurrent submits could all pass the read-then-act guards), and every BUY re-checks positions at order time, not session start. Every run now has a wall-clock deadline (RUN_TIMEOUT_MINUTES, default 30): a hung provider call fails the run with a timeout error instead of leaving it "running" forever, and the last waiter on a shared in-flight analysis cancels it and drops it from the map, so one zombie no longer wedges every later run that joins it. SSE reconnects replay from a snapshot of the event list, so an event emitted mid-replay stops arriving twice; finished runs leave memory after a short reconnect grace once persisted (status, chat, and orders read through run history); automation session writes run off the event loop; and a corrupt session row renders empty instead of blanking the portfolio card. Verified by the new check_lifecycle group (5 concurrent submits yield exactly 1 POST and 4 conflicts, deadline fires on a hung analysis, zombie cleanup, replay dedupe, eviction with DB fallback, mid-session position fills blocked) and the whole suite.

P2.9 Decision-truth and grading fixes (done 2026-10-03)

Shipped as one pass. One grading convention everywhere: entry is the last close on/before the decision date, exit the first close on/after the horizon target (weekend and holiday targets walk forward to the next close, so 1-2 day horizons mature instead of silently vanishing), with the close helpers owned by app/memory.py and imported by the backtest harness so live and replay grading cannot drift. Manager decisions must parse exactly (BUY/HOLD/SELL; "DO NOT BUY" is a parse failure), and failed parses are never recorded, so phantom HOLDs stop polluting the track record. The success-event text is built from the configured horizon at call time; StockAnalysis.confidence is bounded to 0-1; order performance inverts the alpha sign for SELL exits; the decisions migration swallows only duplicate-column errors; and the analysis cache key includes the risk settings so a changed position size stops serving stale results. Verified by all 24 offline check groups and the browser smoke; running uv run python scripts/accuracy.py back-fills previously ungradeable calls.

P2.10 Performance pass (after P1.13)

History reads fully decode every saved run's results JSON through Pydantic on every call, and the market-regime context is downloaded four times per ticker per run with no cache. One pass: decode only the summary fields the listing needs (decision, error, cost, tokens) instead of validating whole StockAnalysis models, one shared "newest analysis for ticker" query with early exit for the previous-call lookups, TTL caches for index downloads and close series (SPY fetched once per run, not per ticker), a stable analysis cache key with eviction on insert, WAL mode plus busy_timeout on every SQLite connection (concurrent writes currently drop decision rows silently), one shared httpx client instead of a fresh one per provider call, and deferred yfinance/alpaca imports for faster startup.

P2.11 Local-safety hardening (first slice of P2.2)

Do this before the app is ever anything but localhost. Require a loopback socket peer in the local-only guard (the Host-header check is spoofable by any non-browser client), validate llm_base_url as https or loopback (today any string is accepted and the API key follows it to that host), add the manager's "treat dossier text as evidence, not instructions" fence to the news, sentiment, bull, bear, and judge prompts (scraped article text currently reaches decision prompts unfenced), and enforce X-Client-ID ownership on run detail, chat, and cancel.

P2.12 Simplify pass (after P1.13)

About 400 lines of dead and duplicated code from the same audit: dead rebuttal branches in bull and bear (the judge replaced the round), the redundant revalidate_assets middleware, manager.to_result with zero callers, unread constants (DISPLAY in all ten agent modules, EQUITY_PERIODS, models.Signal, the stale AGENTS_BY_DEPTH counts), the byte-identical to_result across the six analysts, the six copies of the sqlite _connect helper, hand-rolled correlation and OLS that the statistics module ships, and page bundles that re-implement util.js helpers instead of importing them.

P2.13 Drawdown brake memory: equity high-water mark

The drawdown brake shipped 2026-10-03 (with the position-count cap), but it can only see the unrealized loss on open positions: once a SELL settles, that loss drops out of the P&L the gate reads, so an account that took its drawdown through closed losers looks healthy and BUYs freely, exactly the state a brake exists to stop. Persist a local equity high-water mark, snapshotted best-effort whenever the paper account is fetched, and compare current equity against it in the gate; a deposit or withdrawal resets the mark with a note. Alpaca's own fields cannot source this (realized_pl is today-only, last_equity is yesterday's close), so the local mark is the only honest reference.

P2.14 Run speed: overlap the data phase, budget provider timeouts, time every agent (done 2026-10-14)

Shipped as one scheduling pass with identical decisions. get_stock_data now takes the hosted forecast as a hook and starts it the moment price history lands (it only needs the closes), and the Olostep news fallback starts as soon as Finnhub comes back empty instead of trailing the whole gather by up to its full timeout; the TimeGPT-then-TSFM chain moved into market_data.get_hosted_forecast so the workflow hands one callable down and backtests replay it unchanged. Olostep calls dropped from 45s to a 20s cap (OLOSTEP_TIMEOUT), and every yfinance call in the data phase gained a hard cap (60s for the required history, 20s for the best-effort profile and news) through a wait_for wrapper that degrades instead of hanging. get_market_context is served from a 5-minute TTL cache, so the indices download once per run instead of once per ticker. Every stage's wall time (data fetch, each researcher, debate, judge, manager) lands in StockAnalysis.stage_timings_s, renders in the run footer beside the cost estimate, and any stage over its deliberately generous budget (60s data, 180s researchers, 240s debate/manager, 120s judge) logs a warning. Verified by the new check_run_speed group (overlap ordering, fallback gating, cache hit, timeout fallback, the stage map on a full offline replay) and the whole suite including the browser smoke.

P1.15 Point-in-time fundamentals from SEC EDGAR (as filed) (done 2026-10-12)

Shipped as the keyless as-filed vendor app/tools/sec_edgar.py plus a sec_edgar mode in build_snapshot (CLI: --sec-edgar-fundamentals), so the fundamental analyst runs in replays honestly instead of being dropped. SEC records the filing date of every figure, so a replay dated T reads the statements exactly as they stood that day: a period that has ended but has not been filed yet is not served, and a figure restated later still reads as first reported. Current-vintage exclusion stays the default, the loud look-ahead flag stays on the current opt-in path, and the report labels the as-filed mode as point-in-time with no look-ahead. Verified by the backtest check group (first-reported selection, as-of gate, TTM margins/growth/ROE/EPS, graceful gaps, and a mock replay with the analyst in the roster) and a live two-ticker Max-depth replay against real EDGAR data.

P1.16 Regional benchmarks and multi-market tickers

Alpha is graded against SPY everywhere (app/memory.py, scripts/benchmark.py, app/accuracy.py), which is wrong for any non-US listing and adds FX drift to the scorecard. Add a benchmark resolver (exchange suffix -> regional index, e.g. ^NSEI, ^N225, ^HSI, ^FTSE, ^GSPTSE, ^AXJO, ^BSESN) and route memory, accuracy, and the benchmark script through it; make the benchmark configurable even for US-only use. Pair with exchange-suffixed ticker support so Hong Kong, Tokyo, London, India, A-share, and crypto tickers resolve and benchmark correctly. Do not promote any multi-market result until the P1.2 holdout harness can score it.

P1.17 Settle every due decision, not only the analyzed ticker (done 2026-10-14)

Shipped as a scheduled settle pass. The accuracy sweep was promoted into a reusable memory.settle_pending() that grades every decision whose horizon window has closed, whether or not its ticker gets analyzed again, so a call on a ticker never re-analyzed now reaches the track record, the P1.1 calibration, and the cross-ticker lessons on its own. The pass runs from a sleep-first background loop every MEMORY_SETTLE_INTERVAL_MINUTES (default 60; sleeping first keeps the offline checks download-free) and before every Accuracy page read, which now just settles and aggregates instead of carrying its own grading loop. The grading rules stay owned by app/memory.py; this only changed when they run. Verified by new check_memory sections (a due row matures against synthetic closes, a young row stays open, the pass is idempotent, a row with an unreadable date is never due, the loop spawns and sleeps) and the whole suite.

P2.15 CI gate: offline checks and lint on every push

The repo has no .github/ at all: the scripts/check_*.py suite and the browser smoke only run when a developer remembers uv run test. Add a GitHub Actions workflow that runs uv run test (the offline groups), ruff, and a clean-install import smoke across the supported Python versions, so a broken check or an undeclared dependency fails before merge. Keep the network- and user-file-free property the checks already have.

P2.16 Data breadth: FRED macro and insider transactions

The market-regime analyst reads indices, VIX, and the 10-year yield only, and the fundamental analyst sees a metrics snapshot. Add a keyless FRED macro vendor (CPI, policy rate, unemployment) as news/market input and an insider-transactions input for the fundamental analyst, each optional and degrading to today's behaviour when unavailable. Both are current-vintage by nature; a replay must withhold them rather than leak later prints.

P2.17 Resume an interrupted run from the last completed agent

Interrupted runs are saved and reopenable, but a re-run re-executes every agent. Persist each agent's completed result as it lands and let a resume re-run only the missing or failed slots, so a long Max-depth run that fails at the manager does not repay for the six researchers it already finished. Reuse the durable analysis_runs row and the SSE replay, and clear the checkpoint once the run completes.

P2.18 Bound runaway reasoning: max_tokens and a retry budget

A reasoning model that emits unbounded output can hang a run until the LLM timeout. Add MAX_TOKENS forwarded to every provider and a configurable LLM_MAX_RETRIES, alongside the existing JSON-mode/stream feature-detection and the 429/5xx backoff, so a runaway or a transient rate-limit burst is bounded instead of fatal. Default both to today's effective behaviour.

P2.19 Local Chronos-2 forecast provider (dormant 2026-10-13, owner call)

The Nixtla quota ran dry mid-October, which motivated a quota-free local forecast link. A complete implementation was built and verified on 2026-10-13 (branch feat/local-chronos2-forecast, commit 3579904): get_local_chronos_forecast running Amazon Chronos-2 on CPU through an optional chronos extra, a FORECAST_PROVIDER setting (auto | nixtla | chronos | local), UI labels, stubbed-model units, and a live SPY smoke (0.1s warm inference). The owner then decided against any local time-series model in the app, so the code was stripped from the branch tip and the item parked here. Nothing local remains in the app: the chain is TimeGPT, then TSFM.ai, then the trend model, and backtests blank both hosted keys. To revive: git checkout 3579904 -- app/tools/market_data.py app/workflow.py app/config.py app/settings_store.py .env.example static/js/render.js app/agents/forecast.py app/quality.py app/main.py scripts/check_quick_wins.py pyproject.toml, then re-add the uv sync --extra chronos step and the backtest guard (settings.forecast_provider = "local"). If revived, it must stay out of auto (a torch model reachable from the default chain executes inside the offline checks and wrecks their timing).

Explicitly deferred

  • Local time-series forecasting (P2.19): the owner wants no local forecasting model anywhere in the app; see the dormant note above for the preserved implementation and the revival recipe.

  • Dropping the CrewAI layer (owner decision 2026-10-01: keep CrewAI). The measured case for the swap was that the eight agents are prompt-only calls over precomputed data, exercising no tools, delegation, or memory features, with a heavy dependency tree and a hidden second LLM call on malformed JSON. The call is to keep it: the agent abstraction, LiteLLM provider routing, and the shortest path to real tool use later are worth it, and "powered by CrewAI" is part of the project. If keeping: pin the CrewAI version so a major cannot break the pipeline, and revisit only if dependency weight or the hidden-retry behavior becomes a real problem.

  • More analyst personas: measure the six current researchers first (per-component attribution and cost awareness, arXiv 2603.27539).

  • More debate rounds beyond round one plus the judge: only ship if the paired holdout experiment wins.

  • Trader risk personalities: if ever explored, add personality as a paired-experiment dimension and let the holdout verdict decide.

  • Live chain-of-thought UI: the current optional token stream should stay off by default.

  • Real-money trading or unattended orders: outside this educational project's safety boundary.

  • Gamified streaks, confetti, leaderboards, and pressure notifications: they do not improve decision quality (see the SEC digital engagement statement).

  • A frontend framework migration: revisit only when vanilla code blocks a measured product need.

  • TypeSafe Jev integration: researched 2026-09-23 and parked; the strongest fit was claim verification beside the risk flags, but at single-user scale the savings are pennies. Revisit when per-judgment cost matters or the verification feature is wanted.

  • Tool-calling agents that fetch their own data (TradingAgents runs analysts over tools with a max_tool_rounds cap): this project pre-fetches data and passes a deterministic payload, which is cheaper, faster, and immune to hallucinated tool calls (the upstream changelog is mostly tool-signature and wrong-symbol fixes). Keep the payload design; revisit only if adaptive fetching shows a measured edge in the paired harness.

  • Five-tier rating (Buy / Overweight / Hold / Underweight / Sell): the risk gate, sizing, and the paper-order path are built on ternary BUY/HOLD/SELL, and the track record is graded on direction. Keep ternary unless a paired experiment shows the extra tiers change graded outcomes.

  • A CLI-first headless surface and a LangGraph rewrite: the web UI, SSE stream, and scheduled autopilot already cover unattended runs, and the CrewAI stack is a deliberate keep (see above).

Release gates

Release Must be true
Trustworthy research P0 complete; freshness, coverage, cancellation, keyboard, and mobile checks pass
Evidence-based logic P1.1 and P1.2 complete; a holdout report with baselines and sample sizes exists
Paper execution P1.3, P2.1, P2.3, and P2.8 complete; duplicate, partial-fill, rejection, cancel, sync, and concurrent-order tests pass
Shared deployment P2.2 complete; owner isolation and migration rollback are tested

Research anchors

What the TradingAgents paper (arXiv 2412.20138) actually supports, and what it does not:

  • Debate, reflection, and risk oversight are the load-bearing parts. The deterministic risk gate is this project's testable stand-in for the paper's risk team, extended to concentration (P1.3).
  • The paper's headline numbers are not a benchmark to chase. Three months, three tickers, and Sharpe figures the authors themselves flag as window artifacts. This is why the backtest harness refuses current-vintage fundamentals, demands baselines, holdouts, and bootstrap intervals, and why nothing is promoted on a thin sample.
  • Look-ahead is the known failure mode of LLM agent backtests (Profit Mirage, arXiv 2510.07920; Look-Ahead-Bench, arXiv 2601.13770; Summoning the Oracle, arXiv 2605.24564). The point-in-time defaults and the memorization-risk flag on real-LLM replays stay exactly as strict as they are.
  • Per-component attribution and cost awareness (arXiv 2603.27539) are the instruments for judging additions; the paired harness and the per-run cost estimate exist for that reason.
  • Live arenas are where agent evaluation now happens (Agent Market Arena, arXiv 2510.11695). The paper-trading loop is this project's private version of the same instrument.

The momentum-score citations behind the discovery screen live on Architecture.