Skip to content
All strategies

Strategies

Connors RSI(2) Pullback in the Index-ETF Domain It Was Designed For: Long-Only SPY/QQQ/IWM/DIA When Close > SMA200 and RSI(2) < 5, Exit on Close > SMA5 or 10-Session Clock, Published Parameters Frozen as a Post-2009 Out-of-Sample Test (USEQ 1-DAY)

Outcome: Abandoned

ConnorsRsi2IndexEtfPullbackUseq

Outcome Summary

This run was meant to test Connors' RSI(2) pullback rule on the four broad US index ETFs it was designed for, with post-2009 data as a pre-registered out-of-sample check. The strategy never got past Layer-3 sandbox verification: all three iterations hit the 300s smoke-test limit. In iteration 2 the developer made per-bar indicator work O(1). In iteration 3 profiling pointed to the NautilusTrader catalog file scan, which strategy code cannot shorten. The run was abandoned with no backtest, optimization or analyst verdict, so the hypothesis itself is still untested.

Hypothesis

Source: homily, 'Larry Connors RSI2 mean-reversion strategy', FMZ digest (https://www.fmz.com/digest-topic/5614), sections 'Strategy' steps 1-4 and 'Trading Examples' (DIA). The article describes Connors' RSI(2) rule: take the main trend from a 200-day SMA, buy when RSI(2) falls to 5 or lower above it, and exit on a close above the 5-day SMA. It then ports the rule to OKEx BTC quarterly futures on 15m bars with a 70-bar MA. That port also flips the logic into counter-trend (short RSI2>90 above the MA, long……Show moreShow less

Source: homily, 'Larry Connors RSI2 mean-reversion strategy', FMZ digest (https://www.fmz.com/digest-topic/5614), sections 'Strategy' steps 1-4 and 'Trading Examples' (DIA). The article describes Connors' RSI(2) rule: take the main trend from a 200-day SMA, buy when RSI(2) falls to 5 or lower above it, and exit on a close above the 5-day SMA. It then ports the rule to OKEx BTC quarterly futures on 15m bars with a 70-bar MA. That port also flips the logic into counter-trend (short RSI2>90 above the MA, long RSI2<10 below it). WHAT IS NOT USED, AND WHY: the 15m crypto port. Our corpus has already falsified crypto RSI(2)/short-horizon oversold fades at fee cost: ADA USD-M 2h RSI-2 long-short (1059 trades, PF 0.78), SOL daily RSI oversold (PF 0.69), BTC COIN-M 1H/4H reversion (PF 0.82/0.95), SOL/XRP/DOGE 15-30m reversion (PF 0.68-0.79). Crypto returns have weak or positive short-horizon autocorrelation, so a 2-bar oversold reading has no reversion to sell. WHAT IS USED: Connors' ORIGINAL mechanism on the instruments it was published for, US broad-index ETFs. These have a documented short-horizon negative autocorrelation at the index level: liquidity providers absorb multi-day index selling from ETF, futures and rebalancing flows and are paid a reversal premium. The rules are Connors' published rules with the article's values frozen: SMA200, RSI(2) (Wilder), entry threshold 5, exit close > SMA5. Each ETF is traded as an independent single-leg long-only sleeve. This is NOT a neutral book, NOT a ranking and NOT a rotation. Instruments: SPY, QQQ, IWM, DIA. These are the four broad US index ETFs, and the list is not a hand-picked survivor set: index ETFs do not delist, and DIA is the source article's own example. Histories run 1993/1998/1999/2000 to 2026, about 26 years on the full four-ETF set. The key test is pre-registered. Connors published this rule in 2008-2009 ('Short Term Trading Strategies That Work'), so every bar from 2009 onward is genuine post-publication out-of-sample data. The analyst should report the 2009-2026 slice separately from 1993-2008. A decay from ~0.8%/trade pre-2009 to ~0.3%/trade post-2009 is the expected result. Sub-0.1%/trade post-2009 falsifies the hypothesis. Selection choices, stated honestly (L122): venue USEQ (the only venue where this mechanism has an economic agent paying it); timeframe 1-DAY (the published frequency, and the only USEQ timeframe with decades of history); instruments = the 4 broad index ETFs (the published domain); no gate beyond Connors' own SMA200, which is part of the published rule and not a fitted addition. A variant WITHOUT the SMA200 filter is pre-registered as secondary, for separate scoring only (L130). Flat state: flat about 80-85% of the time per sleeve; each position lasts about 3-5 sessions.

Implements Connors' published rule with the article's parameters frozen (SMA200, RSI(2) <= 5, exit on close > SMA5), plus the hypothesis's 10-session clock, on the four broad index ETFs. Each ETF is its own sleeve; an extra leg acts only on a bar stamped at the primary session close. use_trend_filter=false gives the pre-registered secondary variant without SMA200. Iteration 3: v2 was profiled in the actual sandbox runner. Run time is dominated by NautilusTrader's catalog.bars() fsspec glob over about 126k bar directories on an overloaded host (load ~34). Per-bar strategy work is O(1) per leg, and the strategy ran to completion (1406 bars, 28 SPY entries). The trading logic is unchanged; only documentation changed. The timeout is infrastructure (catalog scan / host load) and should be retried or fixed on the platform side, not in strategy code.

verification_loop: sandbox_timeout: Verification failed (Layer 3 — sandbox backtest): smoke test exceeded the 300s wall-clock limit on a ~20000-bar window (2400 days of SPY.USEQ-1-DAY-LAST-EXTERNAL). This almost always means per-bar work that scales with history — e.g. rescanning the full funding/supplementary series, or rebuilding a list and calling min()/sorted() inside calculate_signal()/on_bar() on every bar. Precompute sorted timestamp arrays ONCE in __init__ and use bisect, or cache lookups keyed by timestamp, so per-bar cost is O(log n) not O(n).

Implementation

Connors RSI(2) pullback, long-only, on four independent USEQ daily sleeves (SPY primary; QQQ/IWM/DIA extras). Entry: close > SMA200 and Wilder RSI(2) <= 5. Exit: close > SMA5 or after 10 sessions. Each sleeve is 25% of equity (max gross 1.0x, inside the 2x Reg-T cap). Whole-share sizing.

Verification Results

sandbox_timeout: Verification failed (Layer 3 — sandbox backtest): smoke test exceeded the 300s wall-clock limit on a ~20000-bar window (2400 days of SPY.USEQ-1-DAY-LAST-EXTERNAL). This almost always means per-bar work that scales with history — e.g. rescanning the full funding/supplementary series, or rebuilding a list and calling min()/sorted() inside calculate_signal()/on_bar() on every bar. Precompute sorted timestamp arrays ONCE in __init__ and use bisect, or cache lookups keyed by timestamp, so per-bar cost is O(log n) not O(n).

Outcome Summary

When a sandbox timeout comes from the catalog/data-loading layer and not from per-bar strategy cost, changing the strategy code will not fix it; it needs an infrastructure fix or a manual escalation, or the verification loop keeps abandoning a valid hypothesis before any backtest runs.

The strategy was abandoned in a verification loop after 3 iterations. Each attempt failed Layer 3 (sandbox backtest) because the smoke test ran past the 300s wall-clock limit on a ~20000-bar SPY.USEQ 1-DAY window. The developer's own profiling blamed ParquetDataCatalog bar-file globbing (~126k bar dirs on a heavily loaded host), not per-bar strategy work.

Test Connors' published RSI(2) pullback rule, with its parameters frozen, as a long-only independent sleeve on each of SPY, QQQ, IWM and DIA (USEQ, 1-day bars): buy when close > SMA200 and Wilder RSI(2) <= 5, and exit when close > SMA5 or after 10 sessions. The rule relies on short-horizon negative autocorrelation in index returns, and data from 2009 onward serves as a pre-registered out-of-sample test.

No performance numbers exist. The backtest report is empty and optimization, backtest review and analyst review never ran. The only figures come from a note in the iteration-3 code: the sandbox smoke test ran 1406 bars and made 28 SPY entries before it hit the time limit.

Backtest and paper results are hypothetical. Trading involves risk of loss.