Skip to content
All strategies

Strategies

Double 7s (Connors/Alvarez via pinkfish, Farrell Aultman) - SPY Daily Close-at-7-Day-Low Buy / Close-at-7-Day-High Sell, SMA200-or-SMA70 Regime Filter, 15% Stop, 2x Reg-T - Faithful Port on USEQ

Outcome: Abandoned

SpyDouble7sPinkfishUseq

Outcome Summary

SpyDouble7sPinkfishUseq attempted a long-only daily SPY mean-reversion approach based on 7-day closing lows and highs, with regime filtering and a stop-loss. It produced no backtest or optimization results because verification failed at Layer 1. After four iterations, the run was abandoned when static analysis identified `_bar_ts` method shadowing; later pipeline stages were not reached.

Hypothesis

Faithful translation of the pinkfish 'double-7s' example strategy by Farrell Aultman (MIT licence, https://github.com/fja05680/pinkfish/blob/18a0d557ead25e0e82de3ece86bcab6c325a59ed/examples/strategies/double-7s/strategy.py). The strategy comes from Larry Connors & Cesar Alvarez, 'Short Term Strategies that Work'. Credit goes to the author and the book. Instrument: SPY.USEQ (the source's own symbol), DAILY bars, long-only, one position at a time. The venue keeps the source's economics: the ETF it was designed……Show moreShow less

Faithful translation of the pinkfish 'double-7s' example strategy by Farrell Aultman (MIT licence, https://github.com/fja05680/pinkfish/blob/18a0d557ead25e0e82de3ece86bcab6c325a59ed/examples/strategies/double-7s/strategy.py). The strategy comes from Larry Connors & Cesar Alvarez, 'Short Term Strategies that Work'. Credit goes to the author and the book. Instrument: SPY.USEQ (the source's own symbol), DAILY bars, long-only, one position at a time. The venue keeps the source's economics: the ETF it was designed for, commission-free US equity execution (~0.02-0.05% RT spread+impact modelled) and Reg-T 2x margin, which matches the source's margin=2. This makes it an exception to lesson 167: the port does not need to change its cost regime, because USEQ IS the regime it was written for. USEQ is 2.5% of the corpus and the mechanism is short-horizon ETF mean reversion, which is not a crypto re-mine. PARAMETERS, the values in the source's main() and strategy.ipynb (the configuration the author actually runs): period=7, sma=70, stop_loss_pct=0.15, margin=2 (leverage 2.0), use_regime_filter=True, use_adj=False. The module-level default_options differ: stop_loss_pct=1.0 (the stop sits at price 0, so it is effectively no stop), margin=1, sma=200, and everything else is the same. Pre-register the default_options set as a SECONDARY variant scored separately. Do not optimise between the two. INDICATORS, all on the daily CLOSE series only (the source drops open/high/low): (1) regime = pinkfish CROSSOVER(fast=1, slow=200). It is positive while close > SMA(close,200), so regime>0 is equivalent to close > SMA200. (2) sma = SMA(close, sma=70). (3) period_high = rolling max of CLOSE over the last 7 bars INCLUDING today. (4) period_low = rolling min of CLOSE over the last 7 bars INCLUDING today. Warm-up: no trading until all indicators are defined, i.e. at least 200 bars (the source uses dropna=True). Data note: our USEQ prices are split-adjusted and dividends are not in PnL. The source uses use_adj=False on raw closes, which also excludes dividends, so the PnL basis is consistent. Expected flat state: in the market roughly 25-35% of days, flat the rest of the time.

Keeps the pre-registered period=7, SMA=70, regime SMA=200, 15% stop, and 2x Reg-T rules unchanged. Iteration 4 only hardens execution: entries are sized from remaining buying power with impact room, and same-bar plus cached open/in-flight guards prevent duplicate entry submissions. The secondary no-stop variant remains separately pre-registered through stop_loss_pct=1.0; no rule variant is mixed into this primary.

verification_loop: Verification failed (Layer 1 — static analysis): - `self._bar_ts = ...` binds a value over framework method `_bar_ts(cls, bar)` — every later call raises "'<type>' object is not callable" (or the framework loses the member). Use a different attribute name. (line 42) - `self._bar_ts = ...` binds a value over framework method `_bar_ts(cls, bar)` — every later call raises "'<type>' object is not callable" (or the framework loses the member). Use a different attribute name. (line 32)

Implementation

Faithful long-only SPY daily Double 7s mean-reversion translation: buy a 7-day closing low when above the 70-day or 200-day regime average, then sell at a 7-day closing high or the 15% anchored stop.

Verification Results

Verification failed (Layer 1 — static analysis): - `self._bar_ts = ...` binds a value over framework method `_bar_ts(cls, bar)` — every later call raises "'<type>' object is not callable" (or the framework loses the member). Use a different attribute name. (line 42) - `self._bar_ts = ...` binds a value over framework method `_bar_ts(cls, bar)` — every later call raises "'<type>' object is not callable" (or the framework loses the member). Use a different attribute name. (line 32)

Leave as-is for a faithful port. The risk reviewer should note that drawdown is dominated by leverage × stop distance.

Position sizing is equity*0.97*2.0 (about 1.94x gross), and the only risk cap is the 15% close-based stop. A stop-out, or a gap through it on daily bars, costs about 29% of equity or more in one trade. This matches the source's margin=2 and is capped at Reg-T 2x with a flat state, so it is not the L148 never-flat compounding pattern. Worst-case gross is 1.94x equity, which is at or below the USEQ max leverage of 2.0.

None required.

Exact float equality c == min/max(window). The window includes today, so this is exact and matches the source. If all 7 closes are identical, both _at_low and _at_high are true. The strategy would then enter and exit on the next bar that is also at the high, which is a harmless degenerate case.

Analysis

Do not change the strategy rules (period=7, sma=70, regime_sma=200, stop 15%, leverage 2). Make two execution fixes. (1) Cap entry notional below buying power: e.g. capital_fraction 0.90, or size from free margin net of impact. Do not re-submit an entry while an entry order is in flight. (2) Re-run and confirm two things: entry_diag.dropped_entry_inflight falls from 136 to about 0, and trades appear in 2001-02..04 and 1998-09..11 when the rules signal. Metric expected to move: annualized_volatility should fall……Show moreShow less

Do not change the strategy rules (period=7, sma=70, regime_sma=200, stop 15%, leverage 2). Make two execution fixes. (1) Cap entry notional below buying power: e.g. capital_fraction 0.90, or size from free margin net of impact. Do not re-submit an entry while an entry order is in flight. (2) Re-run and confirm two things: entry_diag.dropped_entry_inflight falls from 136 to about 0, and trades appear in 2001-02..04 and 1998-09..11 when the rules signal. Metric expected to move: annualized_volatility should fall from 51% toward about 20%, with the paired -X/+X flat-day returns gone. MTM Sharpe should then rise from 0.15 toward the realized-consistent level. Economics are fine: avg_trade 0.69% against a 0.05% USEQ floor, PF 1.56. If the flat-day paired swings persist after the sizing fix, flag the run for engine review rather than optimizing. ## Library refinements (from the knowledge library; test them, do not assume them) The library has no Double 7s source, but it holds Connors/Alvarez RSI2 material from the same book (200-day MA regime, close-based entries, Connors' finding that stops hurt, and an exit on a close below SMA200), plus lumibot guidance on sizing against buying power. It supports the analyst's execution fix: size below buying power and net of the open position. The rule variants belong in separately scored, pre-registered secondaries, not in the primary. 1. [sizing] Size entries against remaining buying power, not full equity x leverage: Keep all signal rules. Change position_size so that qty = floor(min(equity * 0.90 * leverage, free_buying_power * 0.98) / close) - current_open_qty. Here free_buying_power = equity * leverage - open notional, and the 0.98 factor leaves room for USEQ spread and impact. Return 0 if an entry order is already pending or working (check self.cache.orders_open / orders_inflight for the instrument). In practice: capital_fraction 0.97 -> 0.90, plus the in-flight guard. — The analyst found 136 dropped in-flight entries, signalled trades missing in 2001 and 1998, and paired -X/+X flat-day swings that push annualized vol to 51% and MTM Sharpe to 0.15 even though avg trade is 0.69% and PF is 1.56. The lumibot margin-aware example computes the maximum size from portfolio value times a leverage cap, then subtracts current position size to get 'available capacity' before sizing. It stresses that mark-to-market cash is the real buying power, so orders never exceed what the account can carry. At 0.97 x 2.0 the order sits almost exactly at the Reg-T limit, so any adverse move or impact between signal and fill makes it unaffordable. (source: Continuous futures - no expiration to manage (lumibot docs, 'Example: Tracking Available Buying Power') p.1) 2. [stop] Pre-registered secondary: no hard stop (stop_loss_pct = 1.0): Run a separately scored secondary with stop_loss_pct=1.0 (stop disabled) and everything else unchanged: period=7, sma=70, regime_sma=200, leverage 2.0, capital_fraction 0.90. This is the source's own default_options stop setting. Compare stop-exit trade count and PnL with the primary. Do not optimise the stop level. — The RSI2 write-up of Connors' method (Short-Term Trading Strategies That Work) says 'Connors does not advocate the use of stop loss… the use of stop-loss actually damaged performance' in tests of hundreds of thousands of trades. On an at-the-low mean-reversion entry, a fixed 15% stop mostly fires into capitulation lows, which are the best entries for this setup. The 35.7% max drawdown at 2x leverage should show whether the 15% stop actually caps tail loss or just locks it in. This is a claim to test, not proof. (source: RSI2 Mean Reversion Strategy using in futures (Larry Connors RSI2) p.1; Larry Connors拉里·康纳斯 RSI2均值回归策略 p.1) 3. [regime] Pre-registered secondary: exit on a close below SMA200 (regime exit): Run a secondary that adds one exit condition: while long, sell at the close if close < SMA(close,200) AND close < SMA(close,70), i.e. when the entry regime (the 'or' of the two MAs) no longer holds. Evaluate it on the close with no look-ahead. Entries, period=7, the 15% stop and leverage 2.0 stay unchanged. Score it separately from the faithful primary. — The MQL5 port of the Connors RSI2 Classic rules from the same book uses 'Exit buy when: last close price > 5-period moving average, or last close price < 200-period moving average'. The trend filter works as an exit as well as an entry gate. Double 7s currently holds a 2x-levered long through a regime break until either a 7-day closing high or a 15% loss. That is the likely source of the 35.7% drawdown in the 2001-02 and 2008-style declines. The article also warns about fat-tail risk and large floating drawdown for RSI2-type mean reversion. (source: Day Trading Larry Connors RSI2 Mean-Reversion Strategies p.1)

Iteration History

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

Avoid naming instance attributes after framework methods, and verify generated strategy code against the framework API before backtesting.

It was abandoned after four iterations because Layer 1 static analysis found that the instance attribute `_bar_ts` shadowed the framework method `_bar_ts(cls, bar)`, causing later calls to fail.

A long-only SPY.USEQ daily mean-reversion strategy that buys closes at 7-day lows and sells at 7-day highs, with an SMA regime filter and stop-loss.

No backtest report or performance metrics were produced. The strategy did not progress beyond verification.

Abandon Reason

verification_loop: Verification failed (Layer 1 — static analysis): - `self._bar_ts = ...` binds a value over framework method `_bar_ts(cls, bar)` — every later call raises "'<type>' object is not callable" (or the framework loses the member). Use a different attribute name. (line 42) - `self._bar_ts = ...` binds a value over framework method `_bar_ts(cls, bar)` — every later call raises "'<type>' object is not callable" (or the framework loses the member). Use a different attribute name. (line 32)

Analysis

double-7s

Backtest Review

Sharpe
0.15
Total return
4177.01%
Max drawdown
35.73%
Trades
314
Win rate
73.6%
Profit factor
1.56

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