Luck test
A backtest says a rule made money. Would it have made as much on a market with the same moves in a random order? This script shuffles the overnight gaps and the moves inside each day separately, rebuilds the price series, runs the same rule, and counts how often the shuffle does at least as well. A rule that only rides the market's drift scores about the same on shuffles. A real timing edge does not.
What it said about five YouTube strategies
Each dot is one test on SPY daily bars. Green band: p below 0.05. Log scale; 0.001 is the floor at 1,000 shuffles.
| Rule | Period | Profit factor | Shuffled median | p | |
|---|---|---|---|---|---|
| 200-day MA filter | 1993 on | 1.143 | 1.103 | 0.0569 | fails |
| Best MA of 281 lengths | 1993 on | 1.164 | 1.135 | 0.1796 | fails |
| RSI(5) simple | 1993 on | 1.513 | 1.104 | 0.001 | passes |
| RSI(5) double | 1993 on | 1.614 | 1.101 | 0.001 | passes |
| RSI(5) triple | 1993 on | 1.756 | 1.102 | 0.001 | passes |
| RSI(2) below 30 | 1993 on | 1.221 | 1.078 | 0.017 | passes |
| Best RSI(2) of 50 levels | 1993 on | 1.417 | 1.16 | 0.0319 | passes |
| RSI(2) below 9 | 2009 on | 1.266 | 1.133 | 0.2118 | fails |
| RSI(2) below 30 | 2009 on | 1.249 | 1.133 | 0.1039 | fails |
| RSI(5) simple | 2009 on | 1.393 | 1.15 | 0.037 | passes |
| RSI(5) triple | 2009 on | 1.579 | 1.158 | 0.029 | passes |
| 200-day MA filter | 2009 on | 1.158 | 1.145 | 0.3377 | fails |
- Golden cross: 1 of 12 markets under 0.05 after paying for 48 settings (closest USO, p 0.014).
- RSI crossover: 0 of 12 markets under 0.05 after paying for 48 settings (closest SPY, p 0.0619).
Short-term dip buying inside an uptrend survived. RSI(2), published in 2008, passed before and failed after. The trend filter cut drawdowns but did not beat luck. Full write-up.
Use it
pip install numpy python3 luck_test.py prices.csv --rule rsi5 --perms 1000 python3 luck_test.py prices.csv --rule sma --n 200 --from 2009-01-01
Repo: github.com/triton-xxix/proteus-luck-test (MIT). luck_test.py needs only numpy. The CSV needs date, open, high, low, close; an
adjclose column scales prices to total return. Three rules are built in; add your own as a function returning
daily log returns. README.
Method after neurotrader888/mcpt (MIT). No costs or slippage. Not advice. Numbers drawn from results.json by build.py.