The Ledger
One table holds the system together, and it has no foreign keys
Thirteen tables in one SQLite file. Twelve of them are ordinary. The thirteenth — the prediction log — is the pivot on which every self-evaluation mechanism turns, and it is deliberately decoupled from the rest of the schema by a string.
Every table that models the market proper hangs off stocks by integer foreign key: prices, predictions, agent analyses, backtest results. That is the original schema, and it is conventional. Then there is prediction_log, which stores its symbol as a plain string with an index and no relationship at all.
The decoupling is almost certainly deliberate and is the right call. The prediction log is an append-only accountability record, and it must survive a symbol being removed from the watchlist — which the delete route does with a cascade. If the log were joined to stocks, removing a ticker would erase the evidence of every call ever made about it, including the ones that were wrong. Storing the symbol as text means the record outlives the subject.
The cost is that nothing enforces referential integrity on the most important table in the system, and a typo in a symbol produces an orphan row that will never reconcile and never be noticed.
Two of the log's columns carry the whole feedback apparatus. entry_price is captured at the moment the call is made; without it, no return can ever be computed. strategy_id is a free-text label identifying which component made the call. Every downstream mechanism — the Elo tournament, the calibration tracker, the backtester, the adaptive weighting — groups by that string. There is no enumeration constraining it, and no registry of valid values.
Three columns are written later by a scheduled job rather than at insert time: the realised return at one, five and twenty days. A fourth, whether the direction was correct, is filled only for the one-day horizon. Until that job runs, a row is a claim; afterwards, it is evidence.
A fifth column, a hash of the prompt that generated the prediction, exists to join calls back to prompt variants for A/B testing. The hashing code is written and correct. No caller in the repository passes a prompt to it, so the column is universally null and the A/B system that depends on it cannot function.
The prediction log stores its symbol as text so that the record outlives the subject. Delete a ticker and the evidence of every wrong call about it survives.
On the one table with no foreign key