MOEasymmetry← All articles
Research · 2026-06-12 · 6 min read

Why 92% of Our Best Historical Picks Were Ghosts

Track. Study. Wait. Strike.
English อ่านภาษาไทย (Thai)
⚠️ Personal research and trading journal — not investment advice. The author does not provide licensed advisory services.

One of the most reliable tests in quantitative trading is called "Information Coefficient" — the correlation between a signal and future returns. You rank stocks by your signal, measure their returns, and see if higher-ranked stocks outperformed lower-ranked ones. A positive IC tells you the signal has real predictive power.

We use IC to test RS rating: are stocks with RS above 80 today more likely to outperform next month than stocks with RS below 80?

In early testing, the IC for RS rating looked excellent. Top-decile stocks consistently showed up in the top performers. The numbers made sense. The confidence intervals excluded zero.

Then I discovered that 92% of those historically "top-performing" stocks weren't real.

What Stale Data Does to Backtests

The problem is specific to how our RS files are stored. Each stock has a rolling CSV file of its RS ratings over time. When we fetch new data, we update the file with today's RS. When we don't fetch — because the stock was delisted, suspended, or missed a fetch cycle — the file stops updating.

Here's what makes this dangerous: a stock that gets acquired at a 40% premium in 2022 will have its RS spike to 99 right before the acquisition closes, then disappear from the universe. If you're running a historical IC study in 2025, you might look back at January 2022 and find that stock sitting at RS 99 in your data — even though no one could have bought it by the time you're looking.

This is survivorship bias in a specific form: stale data survivorship. The stock "appears" in your historical universe because the file still exists. Its RS is frozen at the last value before it went dormant — often a high value, because stocks with strong RS get acquired more than weak ones.

When I audited our RS file history, I found that 92% of historical "top-10" RS names on certain lookback dates were stale entries — stocks where the RS file hadn't been updated in 30, 60, sometimes 180+ days at the time of the historical "observation."

The IC was measuring real RS winners against a comparison group that included frozen RS from acquired or suspended stocks. Of course the top decile looked good. The comparison was rigged.

The Fix: Freshness Gating

The solution is simple in concept and requires discipline in implementation.

Every RS observation needs a freshness check: how many days ago was this file last updated? If the answer is more than some threshold (we use 5 trading days), the RS for that stock-date pair is treated as missing — not 99, not whatever the last value was, but explicitly missing and excluded from the analysis.

For historical IC studies, this means rebuilding the universe at each date to include only stocks where the RS file was fresh as of that date. Stale files get excluded retroactively.

After applying the freshness gate to our data: - Historical "top-10" names: from 92% stale → under 5% stale - IC dropped from spuriously high to more modest — but now it measures something real - The signal still works, just with honest numbers

The implementation: in fetch_us_rs.py, every RS file write now includes a last_updated timestamp in the file header. Every historical scan or IC calculation checks this timestamp before using the RS value.

Why This Matters for Every Quantitative Trader

If you build RS, momentum, or quality signals from file-based data, you have this problem. Every file that isn't getting updated today is lying about the past.

The stale-data problem compounds with any of these situations: - Delisted stocks — RS goes to 99 before the delist if it was an acquisition, or to 1 before the delist if it was a bankruptcy. Both are misleading historical signals. - Trading halts — Thai stocks suspended for restructuring can sit frozen for months. Their RS looks static, which is artificial. - Fetch failures — A bug in your pipeline, a weekend gap, a ChromeDP timeout. Any missed fetch leaves a stale value. - Universe changes — If you added a new exchange segment or sector to your universe in 2024, historical observations from 2020 are missing those stocks, which skews comparisons.

None of these show up as errors. The data exists. The files are there. The numbers look real. The only way to catch it is to track when each file was last written.

The Broader Principle

Any signal that depends on file-based historical data needs a staleness audit before IC or performance analysis is trusted.

The auditing process: 1. For each file that stores time-series data, record when it was last updated (timestamp in header, or modified-time of the file). 2. For any historical analysis, filter to only include data points where the file was fresh within the analysis window. 3. Rebuild the IC or performance table using only fresh data points. 4. Compare the result before and after the freshness gate. If they're similar, the gate is a confirmation. If they differ significantly — you had stale data, and you were measuring something fictional.

In our case, the IC shifted meaningfully after the gate, which told us the stale-data effect was real and large. The signal survived — RS momentum is real — but the magnitude of the historical numbers had been inflated by ghost stocks we couldn't have actually bought.

What It Looked Like When We Found It

The discovery came when we noticed that several of the "top RS performers" in 2022-2023 were companies we recognized as having been acquired during that period. A few were stocks we'd tried to trade and found they were no longer tradeable.

That inconsistency was the signal. If a stock was "performing" in our historical data but was actually off the exchange, something was wrong with the data pipeline. One audit later, we found 92%.

The fix took a few hours of pipeline work. The lesson took longer to absorb: historical performance data from file-based systems is not reliable by default. You have to build the freshness check in deliberately, because nothing in the data itself tells you it's stale.

Track. Study. Wait. Strike.


Personal research and trading journal — not investment advice. The author does not provide licensed advisory services. — MOEasymmetry

Draft 2026-06-12. Source: RS rating file audit, Thai + US markets. Freshness gate: 5 trading days. Fix wired into fetch_us_rs.py. Stale-data rate: 92% of historical top-10 → <5% after gate. IC was spuriously inflated before fix; valid after.

Get new research by email
Tested across decades. Failures published. Real money.
Subscribe — free
📊 See the live dashboards, the breakout scanner, and the real track record at the MOEasymmetry hub — research, not advice.
← Previous
The Only Thai Signal That Survived Regime Splitting
งานวิจัยและบันทึกการเทรดส่วนบุคคล ไม่ใช่คำแนะนำการลงทุน · Personal research & trading journal — not investment advice. The author does not provide licensed advisory services.
Home · Articles · Methodology · Track record