This site has exactly one load-bearing claim: every pick is written down before the market opens, so nobody — including us — could have known the outcome. On seven occasions that was not true, and we did not notice for six weeks. Here is what broke, what it did to the numbers, and what stops it now.
They remain in the public log — nothing has been deleted — but they are now excluded from every figure on this site. The headline open→close return moves from −2.68% to −3.40%.
A forward-graded record is only worth something if the writing-down genuinely precedes the outcome. Our headline return is the same-day open→close: enter at that morning's opening price, exit at that afternoon's close, net of costs. That number is meaningful only if the pick existed before the opening auction printed.
A pick logged at 09:32 fails that test completely. Not marginally — completely. The opening price it is scored against had already happened. Whatever the scanner saw at 09:32 included information the 09:29 version of it did not have, and the entry price was never available to anyone reading the log.
The scan runs on a GitHub Actions schedule, set for 07:30 ET — two hours of margin before the bell. That felt like plenty.
But scheduled workflows on GitHub are explicitly best-effort. Under load, a job scheduled for 07:30 can start considerably later, and there is no guarantee and no alert. Ours drifted. Usually by seconds. Seven times, by enough to cross the bell:
| Session | Picks | Scan ran | vs 09:30 open |
|---|---|---|---|
| 2026-06-15 | 13 | 09:32 ET | +2 min |
| 2026-07-06 | 16 | 10:42 ET | +72 min |
| 2026-07-07 | 16 | 09:33 ET | +3 min |
| 2026-07-09 | 21 | 10:04 ET | +34 min |
| 2026-07-13 | 21 | 09:34 ET | +4 min |
| 2026-07-27 | 22 | 09:53 ET | +23 min |
| 2026-08-03 | 19 | 09:57 ET | +27 min |
Every affected cohort is late in its entirety. This was never a few stragglers — it was the whole morning scan running after the bell, seven times.
The scanner had guards. It refuses to run on NYSE holidays. It refuses to run when the quote feed returns the previous session's prices unchanged — a check we added after a phantom Juneteenth cohort. What it never did was look at the clock. It checked what the market was doing and what the data looked like, and never asked the one question that its own core claim depended on.
Excluding the late cohorts, on the 596 graded picks:
| Group | n | Mean net | Median net | Win rate |
|---|---|---|---|---|
| As previously published | 596 | −2.68% | −2.72% | 30.9% |
| Timely only (corrected) | 487 | −3.40% | −2.76% | 31.2% |
| Late cohorts | 109 | +0.55% | −2.48% | 29.4% |
The corrected headline is worse. Our screen looks slightly more unprofitable than we had been reporting, which — given that we had already published it as a failure — changes the conclusion not at all and the number visibly.
The tempting story here is: our protocol broke in a way that flattered us. Late picks show +0.55% while timely picks show −3.40%, a four-point gap, and look-ahead is exactly the kind of thing that produces that. It would be a satisfying confession.
We don't think it's true, and the honest thing is to say so.
The median late pick returned −2.48% against −2.76% for timely picks. That gap is a quarter of a percentage point. The mean gap is entirely one trade: JLHL, +241% on 2026-07-09. Remove that single position and the late group's mean falls to −1.67%. Remove the top three and it is −2.99% — indistinguishable from the timely group. The six late cohorts have median returns of −4.07%, −2.88%, −1.10%, −1.65%, −2.83% and −2.26%: all negative, all ordinary.
A four-point gap that vanishes when you delete one row is not evidence of bias. It is one lucky microcap.
Which is a lesson this project has already published once. Experiment 01's central finding was that a mean on financial data can be a single trade wearing a statistic's clothing, and that the way to check is to delete your best result and look again. Here that test says the protocol failure was real and the contamination was not measurable.
So: we broke the rule, the rule matters, and we cannot show you that breaking it helped us. Both halves of that are the finding.
Five of the seven late cohorts fall inside Experiment 01's measurement window. Within that window, 461 graded picks averaged −2.64%; the 365 timely ones averaged −3.65%.
The verdict was FAIL and remains FAIL, by a slightly wider margin. We want to be clear that we are not reporting this because it happens to be harmless — we would be publishing it either way, which is the entire premise of the site. But it is worth stating plainly that no verdict reverses.
1 · A hard pre-open gate. The scanner now resolves the current time in America/New_York and refuses to write anything at or after 09:20 ET — ten minutes before the bell, so a near miss fails loudly instead of landing by luck. A late run produces no picks at all. An incomplete record is recoverable; a contaminated one quietly poisons every statistic downstream for months.
2 · The schedule moved earlier, from 07:30 to 07:00 ET. That is the convenience; the gate is the guarantee. We are no longer relying on a best-effort scheduler to enforce a claim that has to be exact.
3 · The exclusion is derived, not stored. Nothing was flagged by hand. Every surface computes timeliness from the published_at and trading_date columns already in the public CSV, so you can reproduce our exclusion set exactly rather than trusting a flag we set ourselves.
4 · The rule is tested, and the test gates the job. market_time.py ships with a selftest covering both daylight-saving directions — 09:30 ET is 13:30 UTC in summer and 14:30 UTC in winter, and hard-coding either would silently break the check for half the year. It runs in CI before the scanner does.
5 · The weekly audit now checks timeliness, so a recurrence surfaces in days rather than after six weeks.
Everything above re-derives from the public log. No flag, no special access:
import csv, datetime as dt
from zoneinfo import ZoneInfo
ET = ZoneInfo("America/New_York")
late = []
for r in csv.DictReader(open("picks.csv")):
t = dt.datetime.fromisoformat(r["published_at"]).astimezone(ET)
session = dt.datetime.strptime(r["trading_date"], "%Y-%m-%d")
open_et = dt.datetime.combine(session.date(), dt.time(9, 30), tzinfo=ET)
if t >= open_et:
late.append(r)
print(len(late)) # 128
print(sorted({r["trading_date"] for r in late})) # the 7 cohorts
Grab picks.csv and run it. If you get a different answer than we do, tell us — that is the point of publishing the data.
If you keep a forward-graded record of anything, the failure mode generalises: you will guard the thing you are measuring and forget to guard the measurement itself. We checked whether the market was open and whether the data was fresh. We never checked whether we were on time — because the schedule was a setting we configured once and then stopped thinking of as a variable.
Anything scheduled by a best-effort scheduler is a variable. If a deadline is load-bearing for your claim, verify it at write time and refuse to write when you miss it. A gap in the record is a fact you can work with. A silently late entry is a lie you will repeat in every summary statistic until someone else finds it.
Found by an outside review of the raw picks.csv on 2026-08-04. We had published that file for weeks and had not run this check on it ourselves. That is embarrassing and it is also exactly why the file is public.
A verdict lands every few weeks. We'll email you each one, free, the day it publishes — and nothing else. No signals, no picks, no offers. Verdicts stay free and public for everyone either way; this just means you don't have to remember to check back.
One confirmation email first — you're not on the list until you click it. Privacy
Educational and informational only — not investment advice, not a recommendation, and not a broker-dealer. ThePickLog is operated by AMD Ventures, LLC (Florida).
Recorded in the audit log · ← the other bug that flattered our numbers
Disclaimer · Privacy · All experiments