Section VII Corrections & Clarifications Compiled from source

FutureMarket

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

Errata

The register of things that do not do what they say

Every item below was found by reading the code and comparing it against the docstring, comment, or module name that describes it. None is speculative. They are collected here rather than scattered because a reader deciding whether to trust this system deserves them in one place.

There is a particular pattern in this codebase, and naming it explains most of what follows. The author writes the hard part first — the algorithm, the maths, the guard against a subtle failure — and writes it well. Then the wiring that would make it take effect is left for later, and later has not arrived.

The circuit breaker computes exactly which actions a given loss profile demands, and returns them. The caller logs them. The compositor learns which of its sources have been performing and writes the new weights to a table. Nothing reads the table at startup. The backtester slices history into training and test windows with correct geometry. Nothing is fitted to the training window.

In each case the difficult reasoning is present and correct, and a single missing call site makes it inert. That is a specific and recoverable kind of incompleteness, and it is quite different from a system whose ideas are wrong.

A second pattern is subtler and more concerning: the pervasive habit of degrading to a neutral value on failure. A scorer that cannot reach its data returns fifty out of a hundred. A confidence extractor that cannot parse its input returns sixty-five per cent. A risk service that raises returns a midpoint. Each choice is defensible alone — nobody wants a page to fail because one panel is unavailable — but together they make silent failure indistinguishable from a considered neutral opinion, at exactly the point where the two are fused into a trading decision.

Third, and least dramatic: two function names do not resolve. Both sit behind broad exception handlers, so neither crashes anything. Both silently disable a feature that the surrounding code assumes is working.

The tables that follow are grouped by kind rather than by severity, and each row cites where the disagreement lives.

The hard part is written first and written well. The wiring that would make it take effect is left for later, and later has not arrived.

The dominant pattern in this register

Class I

References that do not resolve

Two calls target names that do not exist. Both are wrapped in exception handling broad enough that the failure is invisible at runtime.

Class I — unresolved references
Call siteWhat it asks forConsequence
services/sentiment_aggregator.py:288 A news-fetch method on the data service, named in the plural where the service defines it in the singular The attribute error is swallowed. The highest-weighted sentiment source — nominally half the composite — is permanently unavailable unless a caller supplies headlines directly. The composite silently reduces to a 60/40 blend of the other two sources.
tasks.py:62 A pipeline accessor with a pluralised name; the module defines the singular The background task for deep analysis raises on import every time it runs, retries once, and dies. The in-process scheduler uses the correct name, so the feature works there and only there.
tasks.py:112 Constructs the alpha-factor service with a database session argument That class declares no constructor and accepts no arguments — each method opens its own session. The task raises a type error unconditionally on every invocation.
services/agent_tools.py:99–107 Divides a market-capitalisation field by a billion for display The data service returns that field as an explicit null, so the division raises. A broad handler discards the correctly-built income and balance sections and substitutes an error string, which then flows into the language-model prompt as if it were financial data.

Class II

Documented behaviour that is not implemented

In each case a docstring, comment, or module name promises something the code does not do. These are ordered roughly by how much they matter.

Class II — documentation and implementation disagree
WhereWhat is documentedWhat is implemented
services/circuit_breaker.py
material
A five-state lifecycle including cooldown, and an action list including closing all positions and cancelling all orders on a halt The cooldown state is never assigned by any code path; only the transition out of it exists. The action list is returned to the caller, which writes it to a warning log and stops. Positions in a deep drawdown are not closed. There is no de-escalation from caution or reduced — a manual reset endpoint is the only exit.
services/backtest_engine.py
material
Walk-forward backtesting that “respects bitemporal availability columns to prevent future leakage” The training window is passed into the metric function and used only for its row count. The string available_at does not appear anywhere in the file. Leakage protection is inherited implicitly from the fact that the underlying rows were written live.
services/qlib_ml_pipeline.py
material
A walk-forward routine parameterised by a retraining frequency A single train-and-test split. The retraining frequency parameter is accepted and ignored. Real-time scoring retrains a full model on every call.
services/factor_scoring_engine.py “Adaptive weighting based on recent factor performance” Static weights. The normalisation method that would rebalance them is never called from anywhere.
services/sentiment_aggregator.py Recency decay — a seven-day half-life for news, ninety days for earnings and filings An unweighted arithmetic mean. A comment claims weighting “if dates are available”; the two lines beneath it do not weight.
services/enhanced_sentiment_service.py Four weighted components summing to one, including news volume at a quarter News volume is never summed. It is applied instead as a multiplier on the deviation from the midpoint, ranging from 0.85 to 1.15. The effective denominator is 0.60, or 0.75 when the optional feed is configured.
services/universe_service.py:264 A twenty-entry target-sector filter The branch that would reject an off-target sector ends in a bare pass. The filter rejects nothing.
services/universe_service.py:305 “Use batch download for efficiency” A serial per-symbol loop inside each batch, with a pause between batches. There is no batch download.
services/bitemporal.py:61 A legacy availability fallback of one day Four hours. The backfill migration also uses four hours, so the code is at least self-consistent with itself and not with its documentation.
services/bitemporal.py:139 Stamping price rows with a market-close offset Wall-clock time at insertion. No close-time logic exists in the file.
services/calibration_tracker.py:111 A Beta prior centred on one half A prior centred on each bin's own stated confidence — the tracker starts by assuming perfect calibration. This is arguably the better choice; it is not the documented one.
services/graduation_criteria.py:119 Walk-forward efficiency above fifty per cent on at least two strategies Tournament win rate above fifty-five per cent over at least twenty head-to-head comparisons. The comment states the substitution openly.
services/signal_compositor.py:116 Source signals gathered “in parallel where possible” A sequential await loop. There is no concurrent gather.
services/trade_executor.py:285 An emergency halt that closes all positions and cancels all orders Positions are closed. Orders are not cancelled, and the circuit breaker is not tripped.

Class III

State that does not survive a restart

Several mechanisms learn something and then forget it. In a long-running process this is invisible; across a deployment it silently resets the system's accumulated judgement.

Class III — volatile state
What is lostEffect
Adaptive source weightsWritten nightly to a history table; the constructor unconditionally resets to the hardcoded defaults and nothing reloads the last row. All adaptation is discarded on every restart.
Circuit-breaker state, equity peak, trigger historyHeld in process memory; the module docstring says so. A restart clears a halt, erases the drawdown reference point, and resets the trigger count to zero.
Circuit-breaker “has been tested” evidenceA live-trading graduation criterion depends on a non-zero trigger count. Because that count is volatile, the criterion cannot be satisfied by anything historical.
Daily auto-trade counterIn memory. Also rolls over on the UTC date rather than the exchange's, so the cap resets mid-session in the local afternoon.
Prompt A/B variants and their ratingsEntirely in memory, and never registered by any analysis path in the first place.
Every cacheSeven distinct implementations, all process-local, none size-bounded, none shared with the separate task-queue worker.

The inert feature

The prompt A/B framework deserves separate mention because it is complete and unreachable. It implements variant registration, epsilon-greedy selection with a cold-start clause, Elo rating over pairwise outcomes, and retirement rules. It is exposed over HTTP.

No analysis path selects a variant. No analysis path records an outcome. The persistence story it describes depends on a prompt-hash column that would be populated by passing prompt text to the prediction logger — and no caller passes prompt text. The loop is open at both ends.

One more of this kind: the Elo tournament keeps no watermark of which pairs it has already played. Every nightly run reprocesses the full reconciled history from the beginning, so old matchups compound their effect on ratings indefinitely rather than being counted once.

Class IV

Methodological caveats worth knowing before trusting a number

These are not bugs in the sense of code that fails. They are properties of the maths that make certain outputs mean less than they appear to.

Neutral-on-failure

Three of the five composition sources return a midpoint score when they fail, and two of those failures are routine. At the fusion step, a midpoint contributes exactly zero. A caller cannot distinguish an unavailable source from a source that examined the evidence and had no view.

Confidence is not a probability

The compositor defines confidence as the absolute value of the composite score. It is a magnitude, not a likelihood, and it is then compared against a threshold expressed as though it were a probability, and passed to a calibration tracker that treats it as a stated probability.

Annualisation by prediction count

The backtester's annualised return scales total return by the trading-year constant divided by the number of predictions, rather than the number of calendar days. With several symbols predicted per day, this systematically deflates the figure.

Overlapping-window Sharpe

Alpha-factor Sharpe ratios annualise a five-day forward return by the square root of the trading year over the horizon, on overlapping windows, with no correction for the overlap. The resulting figure is overstated relative to an independent-observation Sharpe.

Additive drawdown

Maximum drawdown is computed on a simple cumulative sum of per-prediction returns rather than a compounded equity curve, and is therefore expressed in return-fraction units. The graduation threshold compares against that quantity.

Look-ahead in the screener backtest

An unwired prototype backtests screener picks by replaying today's universe against every historical window — the as-of date argument is accepted and never used, and the source comment says as much. Survivorship and look-ahead bias are total. Nothing calls this module, and it should not be revived without addressing that first.

Historical news is often unreachable

The news client's historical path over-fetches from a newest-first feed with a hard record ceiling and then filters by date. For older windows, the budget is exhausted before the window is reached, frequently returning nothing. This is the news source used for backtesting.

Factor fitting is not point-in-time

Alpha-factor evaluation reads price history directly rather than through the point-in-time helper that exists a few modules away. The guard is available and simply not applied on that path.

A sign-sensitive alias

One function in the expression sandbox shifts a series by the negation of its argument. With a negative argument it returns a past value, as its comment describes; with a positive one it returns a future value. The expressions are authored by a language model and nothing validates the sign.

Duplicate accounting

An executed trade is written to the prediction log twice under the same strategy identifier — once by the compositor and once by the executor. Skipped and hold decisions are written under that identifier too, so the compositor's own backtest population is dominated by trades it declined to make.

Class V

Dead code, and operational posture

Code with no caller

Modules and components with no live call path
ComponentWhy it is dead
agents/sentiment.py, agents/fundamental.py, agents/supervisor.pyThe original three-agent design named in the README. No endpoint calls the supervisor; the pipeline that replaced it lives elsewhere.
services/screener_backtest.py304 lines, no route, no scheduler job, no test, no reference of any kind outside itself.
services/trading_agents_service.pyDepends on an agent framework that is not installed and whose directory is absent.
backtest_results tableLegacy schema superseded by a newer run table. No code writes to it.
Five frontend componentsA fancier header, an earlier prediction panel, a hex-grid watchlist, a three-dimensional particle scene, and a canvas particle field — roughly 1,100 lines, none imported. The scene component is the sole importer of four heavy graphics dependencies.
Two frontend dependenciesAn animation library and a graphics helper library are declared and never imported anywhere. A third is imported by the dead scene component but not declared at all — it resolves only transitively.

Posture

Stated without alarm, because this is a local research system and not a deployed service — but stated, because the code contains a live trading path.

No authentication anywhere in the request path
An auth router exists with registration, login, and a current-user route. No endpoint depends on it. The frontend never calls it. Every trading endpoint — including emergency halt, position close, and circuit-breaker reset — is reachable unauthenticated.
Permissive cross-origin policy
All origins, all methods, all headers, with credentials allowed.
Health endpoint performs no checks
It returns healthy unconditionally. The status endpoint's feature flags report configuration intent, not availability.
A placeholder signing key ships in source
The JWT signing secret has a default value whose own text instructs the reader to replace it. Since nothing uses the auth system, nothing is currently signed with it — but the default would be live the moment auth is wired up.
Single-writer database under concurrent access
Same-thread checking is disabled, every service dispatches blocking work to threads, and a separate worker process can run the same jobs. Journal mode and busy timeout are left at defaults.
Two schedulers, overlapping
The in-process scheduler registers thirteen jobs; the task-queue beat configuration schedules three of the same ones. Running both double-executes reconciliation, the screener, and forecast precompute.
Nothing gates live trading on readiness
The graduation report is advisory. Paper versus live is decided by one configuration boolean, and the human opt-in criterion is bypassed by another that defaults to enabled.

A closing note

What this register is not

It is not a verdict. A register of this length would be damning for a shipped product and is unremarkable for a research codebase at this stage — roughly twenty-two thousand lines of Python written to explore a question, with the exploration still in progress and the version-control history consisting of a single squashed commit.

The items above cluster in a way that is worth reading as a signal rather than a list. Almost none of them are errors of understanding. The Elo maths is right. The Beta posterior is right. The bitemporal predicate is right. The half-Kelly derivation is right. The sandbox is thoughtfully constructed. The memorisation guard addresses a real and widely-ignored failure mode, and cites the literature that identifies it.

What is missing is the last connection in each case — the call that executes the action, the query that reloads the weights, the fit that uses the training window. That is a different and much easier problem than being wrong about how any of it should work.

The one item that would concern a reviewer most is not on any list above, because it is a matter of arrangement rather than of code: a system that can place orders, with no authentication on the endpoints that place them, and no enforcement gate between its own readiness assessment and its live-trading switch. Everything protective in the design is advisory. That is a safe arrangement while the credentials are absent and the broker is a mock, and it is the first thing that should change if they ever are not.

On what this report omits

  1. The repository is private. No credential, key, token, or connection string appears anywhere in this document. A static scan of the source found no committed live secrets — every credential is read from the environment — and the one default value present is a placeholder whose text identifies itself as such.
  2. Deliberately withheld: the internal deployment cache path, the database filename, the model vendor's endpoint hostname and specific model identifiers, the third-party data vendors' endpoints, and the project's earlier internal codename. Where accuracy required naming a capability, it is described generically — “a hosted inference endpoint” rather than a vendor.
  3. No performance figure of any kind appears in this report. The repository records none: there are no committed backtest outputs, no evaluated model artefacts, and no accuracy history. Every number quoted throughout these seven sections is a configuration default, a class constant, a line count, or a count of code artefacts — each read directly from source and verifiable against it.
  4. Prompt text is described by shape and output requirement rather than reproduced. Hardcoded symbol universes and lexicons are characterised by size and quality rather than listed.