Module G · Strategy Playbook - Chapter 32

Strategy: RSI Pullback & Mean Reversion

A counter-trend pullback system using RSI with a trend filter - buying dips in an uptrend.

StrategyBacktest
What you'll learn
  • ·RSI as a pullback trigger
  • ·Trend filter with EMA
  • ·Entry & exit rules
  • ·Avoiding falling knives
  • ·Backtest & expectancy
  • ·Long-only vs both sides

Most of the systems in this playbook have been trend followers - they buy strength and sell weakness. This one flips that instinct on its head. A mean-reversion pullback system waits for a strong stock to take a breather, dip against the trend, and then buys that dip the moment momentum turns back up. The bet is simple: in a healthy uptrend, shallow pullbacks tend to get bought, so we try to join the next leg early.

Good to know

RSI was introduced by J. Welles Wilder in 1978 and is bounded between 0 and 100. A pullback system uses it the opposite way to a breakout one - buying weakness that appears inside strength.

The obvious danger is that some dips are not pauses at all - they are the first step of a real decline. This is where a trend filter earns its keep. By only buying pullbacks while price is above a long moving average, we refuse to "catch a falling knife" in a downtrend and concentrate our entries where the wind is at our back. Let us build the whole thing, end to end.

The idea in three rules

A pullback system is easy to say out loud, which is a good sign. We need exactly three decisions:

  1. Are we allowed to trade at all? Only if the bigger trend is up - we use Close > EMA(Close, 200) as the gate.
  2. Has price actually pulled back? We use RSI dropping below a mild oversold level (40, not the usual 30) to mark a dip within the uptrend.
  3. Has momentum turned back up? We wait for RSI to tick higher than the previous bar, so we are buying the recovery, not the falling part of the dip.

RSI is the perfect tool here because in an uptrend it rarely reaches the classic 30 oversold line - the dips are too shallow. A threshold of 40 catches those polite, buyable pullbacks that a 30 line would miss entirely.

Note

This is a counter-trend entry inside a with-trend filter. The entry (buy a dip) leans against the short-term move, but the filter (price above its 200 EMA) makes sure the long-term move is still on our side. That combination is what separates disciplined mean reversion from blindly buying every red candle.

The complete system

Here is a clean, runnable long-only system. Read it once top to bottom - it follows the same shape as every system in this course.

_SECTION_BEGIN("RSI Pullback & Mean Reversion - Long Only");

// === Housekeeping ===
SetChartOptions(0, chartShowArrows | chartShowDates);
SetTradeDelays(1, 1, 1, 1);          // signal on the close, fill next bar's open
SetPositionSize(1, spsShares);       // one share per trade for a clean test

_N(Title = StrFormat("{{NAME}} - {{INTERVAL}} {{DATE}}  O %g  H %g  L %g  C %g (%.1f%%)  {{VALUES}}",
    O, H, L, C, SelectedValue(ROC(C, 1))));

Plot(Close, "Close", colorDefault, styleNoTitle | styleCandle);

// === Inputs ===
rsiLen   = Param("RSI Length", 14, 2, 50, 1);
dipLevel = Param("Dip Level", 40, 10, 50, 1);
trendLen = Param("Trend EMA", 200, 50, 300, 10);

// === Indicators ===
myRSI   = RSI(rsiLen);
trendMA = EMA(Close, trendLen);
Plot(trendMA, "Trend EMA", colorAqua, styleThick);

// === Rule 1: trend filter (only buy dips in an uptrend) ===
upTrend = Close > trendMA;

// === Rule 2 + 3: RSI dipped, then turned back up ===
dipped = Ref(myRSI, -1) < dipLevel;   // RSI was below the dip level last bar
turnUp = myRSI > Ref(myRSI, -1);      // RSI is rising again now

Buy = upTrend AND dipped AND turnUp;

// === Exit: momentum recovered, or the trend broke ===
Sell = Cross(myRSI, 60) OR Close < trendMA;

// === Clean the signals so each fires once ===
Buy  = ExRem(Buy, Sell);
Sell = ExRem(Sell, Buy);

BuyPrice  = Open;                     // trade delay fills us at the next open
SellPrice = Open;

// === Mark entries and exits ===
PlotShapes(IIf(Buy,  shapeUpArrow,   shapeNone), colorGreen, 0, Low,  -20);
PlotShapes(IIf(Sell, shapeDownArrow, shapeNone), colorRed,   0, High,  20);

_SECTION_END();

Notice the two honesty habits we built earlier in the course. dipped uses Ref(myRSI, -1), so the trigger only looks at bars that have already closed. And SetTradeDelays(1, 1, 1, 1) with BuyPrice = Open means a signal computed on tonight's close is filled at tomorrow's open - never at a price we could not actually have traded.

Why the trend filter matters so much

Strip the upTrend condition out and you have a naive "buy every RSI dip" system. It will happily buy dip after dip all the way down a bear market, each one looking like a bargain and each one getting cheaper. That is the falling-knife trap, and it is how mean-reversion systems blow up.

The 200-period EMA acts as a single yes/no switch. Above it, dips are pauses in an uptrend and tend to resolve upward - exactly the behaviour we are paying for. Below it, dips are continuation of a downtrend, and we simply stand aside. One line of code removes the worst trades a dip-buyer can make.

Heads up

A trend filter reduces your number of trades, sometimes dramatically. That is the point, not a bug. Fewer, higher-quality entries beat a flood of marginal ones. If your filtered system barely trades, loosen the filter (a shorter EMA) before you abandon it - do not remove the safety rail altogether.

Backtest and expectancy

Run this in the Analysis window as a backtest over a liquid name like RELIANCE or an index proxy, and read the report the way Chapter 25 taught. For a pullback system, three numbers matter most:

  • Win rate - mean-reversion systems often win 55-70% of trades because they aim for small, frequent gains.
  • Payoff ratio - the average win divided by the average loss. Dip-buyers usually run a payoff below 1, which is fine as long as the win rate is high enough to compensate.
  • Expectancy - the single number that ties them together.

Expectancy is the average profit you can expect per trade:

Expectancy = (Win% x AvgWin) - (Loss% x AvgLoss)

If your system wins 62% of the time, makes 1.4% on winners and loses 1.6% on losers, then expectancy is (0.62 x 1.4) - (0.38 x 1.6) = 0.868 - 0.608 = +0.26% per trade. A small positive number, repeated over hundreds of trades with discipline, is precisely what an edge looks like.

Key idea

A high win rate alone proves nothing - a system that risks 5% to make 0.5% can win 80% of the time and still bleed money. Always judge a pullback system by expectancy, which folds win rate, average win and average loss into one honest figure.

Long-only versus both sides

We built this long-only, and for most equity traders that is the right call. Stocks and indices have an upward drift over time, short-selling has extra cost and risk, and the cleanest version of "buy the dip" lives on the long side.

If you want a symmetric system, mirror every rule: trade short pullbacks only when Close < EMA(Close, 200) (a downtrend), trigger on RSI poking above a mild overbought level like 60 and then turning back down, and cover when momentum recovers. Add Short and Cover arrays alongside Buy and Sell exactly as you did for the EMA crossover in Chapter 27.

Tip

Before going symmetric, backtest the long and short halves separately. Very often the long side carries a clean edge while the short side barely breaks even - and you would rather know that than let a weak short book quietly drain a strong long book.

Try it yourself

  • Run the system on NIFTY and on a single stock like RELIANCE; compare how often each one trades.
  • Set the Dip Level parameter to 30 and then to 45 - watch the trade count change and find the sweet spot for your symbol.
  • Temporarily delete the upTrend condition and re-run the backtest over a falling period to see the falling-knife problem in the trade list.
  • Add an ApplyStop percentage stop (Chapter 22) and check whether it improves or hurts expectancy.

Recap

  • A pullback system buys a dip within an uptrend, betting that shallow pullbacks get bought.
  • RSI below a mild level (40) marks the dip; RSI turning back up confirms momentum has recovered.
  • The Close > EMA(Close, 200) trend filter is what stops you catching falling knives.
  • Judge the result by expectancy, not by win rate alone.
  • Long-only is the natural fit; if you go both sides, test each direction on its own first.

Next we step into Module H and take a system live - starting with reading a higher time frame on a lower-time-frame chart.