Strategy: RSI Pullback & Mean Reversion
A counter-trend pullback system using RSI with a trend filter - buying dips in an uptrend.
- ·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.
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:
- Are we allowed to trade at all? Only if the bigger trend is up - we use
Close > EMA(Close, 200)as the gate. - Has price actually pulled back? We use
RSIdropping below a mild oversold level (40, not the usual 30) to mark a dip within the uptrend. - Has momentum turned back up? We wait for
RSIto 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.
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.
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.
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.
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
NIFTYand on a single stock likeRELIANCE; compare how often each one trades. - Set the
Dip Levelparameter to 30 and then to 45 - watch the trade count change and find the sweet spot for your symbol. - Temporarily delete the
upTrendcondition and re-run the backtest over a falling period to see the falling-knife problem in the trade list. - Add an
ApplyStoppercentage 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.
RSIbelow a mild level (40) marks the dip;RSIturning 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.