Skip to content
All library documents

Double Seven Mean Reversion with a Regime Filter

Article Strategy library · Author: Farrell Aultman (pinkfish)

Summary

The document describes a short-term mean-reversion strategy, originally designed for ETFs, that buys after a close reaches the low of a chosen lookback period and sells when it reaches that period’s high. It also exits if price falls below a stop level set from the entry price, or at the end of the data. A regime condition uses a crossover and a moving average to restrict entries; the options allow changing the lookback, stop loss, margin, and filter settings.

The supplied example applies the method to SPY and sets a shorter moving-average regime threshold than the default. The document provides implementation details and configurable parameters, but no backtest results or performance evidence. Its explanation suggests the filter can be overridden to re-enter earlier after a decline, though this is a discretionary design choice rather than a validated claim. Results may depend on the security, data, costs, parameter selection, and execution assumptions.

Key ideas

  • The strategy buys when the close reaches the lowest value in its selected lookback window.
  • It sells when the close reaches the lookback high, breaches the entry-based stop level, or the data ends.
  • A moving-average regime condition can restrict new entries.
  • The example configures the strategy for SPY with a shorter regime average and margin above one.

Tags

Full text
# double-7s


# double-7s









The double-7s stategy.

The simple double 7's strategy was revealed in the book
'Short Term Strategies that Work: A Quantified Guide to Trading Stocks
and ETFs', by Larry Connors and Cesar Alvarez. It's a mean reversion
strategy looking to buy dips and sell on strength and was initially
designed for ETFs.

This module allows us to examine this strategy and try different
period, stop loss percent, margin, and whether to use a regime filter
or not.  We can also overide the regime filter and start trading again
by setting `sma` below 200 (50-100 works well for this).  The idea is
that then security has bottomed and there are some opportunities for
good trades before the regime filter would allow us to trade again.

## Source (MIT)

```python
"""
The double-7s stategy.

The simple double 7's strategy was revealed in the book
'Short Term Strategies that Work: A Quantified Guide to Trading Stocks
and ETFs', by Larry Connors and Cesar Alvarez. It's a mean reversion
strategy looking to buy dips and sell on strength and was initially
designed for ETFs.

This module allows us to examine this strategy and try different
period, stop loss percent, margin, and whether to use a regime filter
or not.  We can also overide the regime filter and start trading again
by setting `sma` below 200 (50-100 works well for this).  The idea is
that then security has bottomed and there are some opportunities for
good trades before the regime filter would allow us to trade again.
"""

import datetime

import pandas as pd

import pinkfish as pf


pf.DEBUG = False

default_options = {
    'use_adj' : False,
    'use_cache' : True,
    'stop_loss_pct' : 1.0,
    'margin' : 1,
    'period' : 7,
    'sma' : 200,
    'use_regime_filter' : True
}

class Strategy:

    def __init__(self, symbol, capital, start, end, options=default_options):

        self.symbol = symbol
        self.capital = capital
        self.start = start
        self.end = end
        self.options = options.copy()

        self.ts = None
        self.tlog = None
        self.dbal = None
        self.stats = None

    def _algo(self):

        pf.TradeLog.cash = self.capital
        pf.TradeLog.margin = self.options['margin']
        stop_loss = 0

        for i, row in enumerate(self.ts.itertuples()):

            date = row.Index.to_pydatetime()
            close = row.close 
            end_flag = pf.is_last_row(self.ts, i)

            # Sell Logic
            # First we check if we have any shares, then
            #  - Sell if price closes at X day high.
            #  - Sell if price closes below stop loss.
            #  - Sell if end of data.

            if self.tlog.shares > 0:
                if close == row.period_high or close < stop_loss or end_flag:
                    if close < stop_loss:
                        print('STOP LOSS!!!')
                    self.tlog.sell(date, close)

            # Buy Logic
            #  - Buy if (regime > 0 or close > row.sma) or not using regime filter)
            #            and price closes at X day low.

            else:
                if (((row.regime > 0 or close > row.sma) or not self.options['use_regime_filter'])
                        and close == row.period_low):
                    self.tlog.buy(date, close)
                    # Set stop loss.
                    stop_loss = (1-self.options['stop_loss_pct'])*close

            # Record daily balance.
            self.dbal.append(date, close)

    def run(self):
        
        # Fetch and select timeseries.
        self.ts = pf.fetch_timeseries(self.symbol, use_cache=self.options['use_cache'])
        self.ts = pf.select_tradeperiod(self.ts, self.start, self.end, use_adj=self.options['use_adj'])

        # Add technical indicator: 200 sma regime filter.
        self.ts['regime'] = pf.CROSSOVER(self.ts, timeperiod_fast=1, timeperiod_slow=200)

        # Add technical indicator: X day sma.
        self.ts['sma'] = pf.SMA(self.ts, timeperiod=self.options['sma'])

        # Add technical indicator: X day high, and X day low.
        self.ts['period_high'] = pd.Series(self.ts.close).rolling(self.options['period']).max()
        self.ts['period_low']  = pd.Series(self.ts.close).rolling(self.options['period']).min()
        
        # Finalize timeseries.
        self.ts, self.start = pf.finalize_timeseries(self.ts, self.start, dropna=True, drop_columns=['open', 'high', 'low'])
        
        # Create tlog and dbal objects.
        self.tlog = pf.TradeLog(self.symbol)
        self.dbal = pf.DailyBal()

        # Run algo, get logs, and get stats.
        self._algo()
        self._get_logs()
        self._get_stats()

    def _get_logs(self):
        self.tlog = self.tlog.get_log()
        self.dbal = self.dbal.get_log(self.tlog)

    def _get_stats(self):
        self.stats = pf.stats(self.ts, self.tlog, self.dbal, self.capital)


def main():
    """Run a backtest with the same defaults as strategy.ipynb."""
    symbol = 'SPY'
    capital = 10000
    start = datetime.datetime(*pf.SP500_BEGIN)
    end = datetime.datetime.now()
    options = default_options.copy()
    options.update({
        'stop_loss_pct': 0.15,
        'margin': 2,
        'period': 7,
        'sma': 70,
    })
    s = Strategy(symbol, capital, start, end, options)
    s.run()
    pf.print_full(s.stats)


if __name__ == '__main__':
    main()

```

Shown in full with attribution under the source's licence. Licence: MIT

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.