Vol. I · No. 1 A Field Report on a Private Codebase Backend & Frontend Audit

FutureMarket

An autonomous equity-research loop — ingestion, scoring, execution, and self-grading — described from the source

Leader · What the system actually is

A research pipeline that argues with itself, then keeps the receipts

FutureMarket is a single-node Python service that pulls free market data, runs it through a mixture of arithmetic indicators and large-language-model analysts, writes every resulting opinion to a log, and — days later — goes back to mark its own homework. The self-grading half is the interesting part. The prediction half is mostly ordinary.

Most systems that claim to predict markets are opaque about the join between the claim and the outcome. FutureMarket is unusual in that the join is a database table. Every prediction any component emits — technical, sentiment, LLM debate, factor model — is written to a single prediction_log row carrying a strategy_id, a stated confidence, and the price at the moment of the call. A scheduled job returns one, five, and twenty days later to fill in what actually happened.

Everything downstream is built on that table. An Elo tournament pairs strategies that made calls on the same symbol on the same day and rates them head-to-head. A calibration tracker asks whether a strategy that says “70% confident” is right about seventy per cent of the time, and derives a multiplier that shrinks or inflates future confidence accordingly. A signal compositor then weights the five live prediction sources by exactly those two numbers before it fuses them into a single score.

That is a genuine, closed feedback loop, and it is implemented rather than merely diagrammed. It is also, at time of writing, a loop with several severed strands. The circuit breaker computes the actions it wants taken and then only logs them. The walk-forward backtester never trains anything on its training window. Adaptive weights are recomputed nightly and thrown away on the next process restart, because nothing reloads them.

This report describes the system as it is in the source, not as its docstrings describe it. Where the two disagree — and they frequently do — the disagreement is reported. A section of Errata collects the cases where a module promises behaviour it does not implement.

The honest summary: FutureMarket is a well-structured harness for evaluating market predictions, wrapped around predictors that are individually unremarkable. The scaffolding is more sophisticated than the alpha. Whether that is a criticism depends on which half you were trying to build.

No performance figures appear anywhere in this report. The repository contains no recorded returns, no committed backtest output, and no evaluated model artefacts — only the code that would produce them. Publishing numbers would mean inventing them.

HTTP endpoints
76across 13 routers
Service modules
43backend/services
ORM tables
13SQLite, Alembic-managed
Scheduled jobs
1311 fixed, 2 gated
Test functions
1347 pytest modules
Frontend views
9no router library

Figure 1 · System topology

Where the code lives

The backend is a single FastAPI process. There is no service mesh and no queue in the critical path — APScheduler runs thirteen jobs inside the web process itself, and a parallel Celery configuration exists but schedules only three of them. Module names below are the real files on disk.

Figure 1 Module topology and data flow, backend/
FutureMarket backend module topology A five-band vertical diagram. External data providers feed ingestion service modules, which feed scoring modules, which write to a SQLite persistence layer. The signal compositor reads the scoring layer and drives the trade executor through a circuit breaker. A feedback band returns reconciled outcomes to the compositor's weighting. A surface band exposes FastAPI routers to a React single-page application. EXTERNAL PROVIDERS yfinance / OpenBB GDELT DOC 2.0 SEC EDGAR DuckDuckGo Hosted LLM endpoint Alpaca (paper) Qlib INGESTION & ENRICHMENT openbb_service gdelt_service sec_filing_analyzer agent_tools insider · options_flow universe_service · risk_service SCORING & ANALYSIS factor_scoring_engine 5 factors → 0–100 alpha_factor_service LLM-authored formulas nim_agents_service 5-stage debate sentiment_aggregator FinBERT · earnings · SEC qlib_service falls back to rules PERSISTENCE — SQLite, single file prediction_log stock_prices alpha_factors strategy_ratings backtest_runs signal_weight_log + 7 more DECISION PATH — gated on autonomy_enabled signal_compositor weighted fuse of 5 sources circuit_breaker 5-state gate trade_executor half-Kelly sizing alpaca_trading_service bracket order · paper FEEDBACK — nightly, in order reconciliation_service fills actual_return_1d/5d/20d strategy_tournament Elo, K=32/16 calibration_tracker Brier · Beta posterior backtest_engine weekly, windowed Elo × calibration → source weights SURFACE FastAPI — 13 routers, 76 endpoints CORS open · no auth on trading routes React 19 SPA — 9 views, Zustand no router library; view is store state APScheduler — 13 jobs, in-process Celery beat schedules 3 of them All of the above runs in one process, against one SQLite file, on one machine.
Figure 1. The whole backend, by band. Solid rules are data flow; dashed vermilion is the feedback path that turns reconciled outcomes back into source weights. Dashed outlines mark components that degrade to a neutral score rather than fail — qlib_service returns 50/HOLD when the optional dependency is absent, which is indistinguishable at the call site from a genuine neutral reading. Derived from backend/main.py:17–51, 85–200 · backend/services/*.py · backend/api/*.py

The claim and the code

What is load-bearing, what is scaffolding

The repository presents itself, in its README, as a “cyberpunk-themed stock market analysis application powered by multi-agent AI predictions.” That README is stale — it describes a six-stock demo with an Anthropic-backed three-agent supervisor that no longer runs. The agents/ directory it points at contains three classes that no endpoint calls.

What actually runs is larger and more careful than the README suggests, and less magical. There is no trained model anywhere in the served path. The two ML-adjacent modules — qlib_service and qlib_ml_pipeline — both depend on an optional package and a local dataset; when either is missing, scoring silently falls through to hand-tuned arithmetic on RSI, MACD, and moving-average crossovers.1

The five-stage LLM pipeline is the most substantial original component, and it is genuinely a pipeline rather than a prompt: seven data fetches are performed first with free sources, then exactly five model calls run in sequence, each seeing the prior stages' output. Only the fifth is parsed for structure. The other four exist to shape the fifth.

The strongest engineering in the repository is not in prediction at all. It is in bitemporal.py, entity_neutering.py, reconciliation_service.py, and calibration_tracker.py — four modules concerned entirely with not fooling yourself. They implement point-in-time query guards, identity-stripping so a model cannot recall a memorised price path, ground-truth backfill, and Brier-scored confidence correction respectively. Three of the four carry citations to published papers in their docstrings.

Four modules exist purely to stop the system fooling itself. They are better built than the modules that make the predictions.

On bitemporal.py, entity_neutering.py, reconciliation_service.py, calibration_tracker.py

Maturity, stated plainly

This is a solo research codebase, not a product. It runs against paper trading with mock credentials by default. It has 134 tests covering seven modules and none covering the other thirty-six. Version-control history is a single squashed commit. Several documented behaviours are unimplemented, and two cross-module function names do not resolve at all.

It is a serious piece of exploratory engineering with a substantial amount of unfinished wiring — which is what that stage of a project looks like.

Capability register — verified against source, not documentation
CapabilityStatusEvidence in source
Market data ingestion, caching, technicalsWorkingservices/openbb_service.py — five cached fetch paths, per-call TTLs
Prediction logging with entry priceWorkingservices/prediction_logger.py — writes every call, caller owns the transaction
Outcome reconciliation at 1/5/20 daysPartialAll three horizons read the same live quote; a missed run leaves a horizon permanently null
Elo strategy tournamentPartialCorrect Elo maths, but no idempotency watermark — replays full history nightly
Confidence calibration & shrinkageWorkingservices/calibration_tracker.py — Beta posterior, Brier score, clamped multiplier
Signal composition across 5 sourcesWorkingservices/signal_compositor.py — Elo × calibration weighting, threshold gates
Position sizing & bracket executionPartialHalf-Kelly implemented; runs against a mock broker when credentials are absent
Circuit-breaker risk haltsAdvisory onlyState machine blocks new entries; its emitted actions are logged, never executed
Walk-forward backtestingMisnamedWindowed out-of-sample scoring; the training window is counted, never trained on
LLM-generated alpha factorsWorkingservices/alpha_factor_service.py — sandboxed expression eval, IC/ICIR lifecycle
Prompt A/B testing with EloInertImplemented and exposed over HTTP, but no analysis path ever calls it
Authentication on trading routesAbsentAuth router exists; no endpoint depends on it and the frontend never calls it

Boot record

What the process prints when it starts

Startup runs Alembic to head, registers the scheduler jobs with deliberately staggered first-run offsets — a documented measure against a thundering herd of upstream data requests — then opens the trade stream. The job list below is printed by a loop over _scheduler.get_jobs().

backend — uvicorn
$ uvicorn main:app --reload
INFO:     Will watch for changes in these directories: ['.../backend']
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO:     Started reloader process using WatchFiles

Starting StockVision API...
  Alembic migrations applied successfully
Database initialized successfully!
APScheduler started: screener every 12h, forecast precompute every 24h,
reconciliation every 24h, auto-predictions every 4h, auto-sentiment every 24h,
auto-deep weekly, tournament every 24h, calibration every 24h, alpha lifecycle
weekly, circuit breaker P&L hourly, walk-forward backtests weekly
  Job: Daily Reconciliation      → next run: +00:02:00
  Job: Forecast Precompute       → next run: +00:05:00
  Job: Circuit Breaker P&L Update → next run: +00:08:00
  Job: Strategy Tournament       → next run: +00:17:00
  Job: Calibration Update        → next run: +00:22:00
  Job: Adaptive Weight Update    → next run: +00:27:00
  Job: Auto Quick Predictions    → next run: +00:35:00
  Job: Signal Compositor & Auto-Execution → next run: +00:45:00
  Job: Stock Screener            → next run: +01:05:00
  Job: Auto Sentiment Analysis   → next run: +01:35:00
  Job: Auto Deep Analysis        → next run: +02:05:00
  Job: Alpha Factor Lifecycle    → next run: +02:35:00
  Job: Walk-Forward Backtests    → next run: +03:05:00
Trade stream failed to start: alpaca-py not configured — mock mode
INFO:     Application startup complete.

The job names and interval summary are the literal strings emitted at backend/main.py:201–209; offsets are the next_run_time deltas registered at lines 85–198. Timestamps are shown as relative offsets rather than invented wall-clock times.

Notes

  1. The rule-based fallback is a fixed additive form starting from a neutral 50 — RSI bands, MACD histogram sign, a 20/50 moving-average crossover term, and a Bollinger position term. Its response carries "model": "rule-based", so the substitution is at least declared to the caller. See The Desk.