Moving Average Pullback Backtester

Test whether moving average pullbacks are effective, and if so, which moving average is best for your favorite markets.

36 mins
Beginner-Friendly
youtube-video-thumbnail
Table of Contents
    Add a header to begin generating the table of contents
    Table of Contents
      Add a header to begin generating the table of contents

      In this thinkScript tutorial, we’ll build a POWERFUL Moving Average Pullback Backtester for ThinkOrSwim, in less than 35 minutes.

      Moving Average Pullback - TOS Indicators

       

      We’ll design this backtester to help us answer the following questions:

      1. Does buying / selling pullbacks into moving averages really work?

      2. If so, which moving average is most effective for a particular market?

      3. And, should we wait for a confirmation, or enter blindly with a pullback?

      This is a beginner-friendly tutorial, and I’ll cover the following:

      • How to Build a “Back of the Napkin” Backtester Easily
      • How to Define Clear Entry and Exit Signals
      • How to Correctly Use the AddOrder() function
      • How to Use the Switch Function to Test Different Scenarios
      • How to Backtest Small Changes in a Strategy to Find BIG Improvements in P/L

      I’ll walk you through step-by-step, how you can build a similar backtester, and we’ll also review some examples towards the end of the tutorial.

      Let’s get started!

      Volatility Box Invite

      We are TOS Indicators.com, home of the Volatility Box.

      The Volatility Box is our secret tool, to help us consistently profit from the market place. We’re a small team, and we spend hours every day, after the market close, doing nothing but studying thousands of data points to keep improving and perfecting the Volatility Box price ranges.

      We have two different Volatility Boxes - a Futures Volatility Box and a Stock Volatility Box.

      Futures Volatility Box - Trade Major Markets With an Edge

      Designed For: Futures, Micro-Futures and Index Market Traders
      Supported Models: Hourly Volatility Box models
      Supported Markets: 10 Major Futures Markets

      The Futures Volatility Box comes with:

      • 5 Volatility Models for each market
      • Support for 10 Futures Markets (/ES, /NQ, /YM, /RTY, /CL, /GC, /SI, /ZB, /HG, /NG)
      • Video Setup Guide
      • Trade Plan
      • Access to all members-only resources, including Squeeze Course

      Learn More About the Futures Volatility Box

      Trade futures and micro futures with a consistent volatility edge

      Stock Volatility Box - Powerful Web Based Volatility Platform

      Designed For: Stock and Options Traders
      Supported Models: Hourly and Daily Volatility Box models
      Supported Markets: 10,000+ Stocks and ETFs (new markets added frequently)

      A Stock Volatility Box membership includes access to: 

      • Live Scanner - A powerful scanner we've built from scratch, to scan 10,000 symbols every 2 seconds for new volatility breaches
      • Dashboard - A quick and easy way to view daily volatility model levels online
      • Short Interest Scanner - Short interest, Squeeze, and EMA data to find short squeezes
      • Squeeze Course - All of our proprietary squeeze tools, including robust backtesters
      • All Members Only Indicators - We don't nickel and dime you. Everything really is included.
      • And much more!

      Learn More About the Stock Volatility Box

      Trade stocks and options with a consistent volatility edge

      S&P 500 – Which Moving Average Pullback is Best?

      Let’s look at an example of using the backtester inside of the S&P 500, to find which moving average pullback leads to the greatest profits.

      Here are the parameters used for this backtest (if you’d like to follow along):

      • Symbol: SPY
      • Length: 5-Years
      • Type: Longs Only
      • Target: 3 * ATR
      • Stop: 2 * ATR

      Using those parameters, here is what we found, through our backtest:

      S&P 500 - Best Moving Average for a Pullback

      If you used an 8-period Exponential Moving Average (EMA) to trade the SPY, your P/L would have been almost $4,300 LOWER if you did not wait for a confirmation, but instead tried to BLINDLY buy a pullback!

      Put a different way – you could have increased your P/L by almost 300% by simply waiting for price to confirm the pullback entry.

      On the other hand, had you used a 34-period EMA, your P/L, while still less than the 8-EMA pullback, buying blindly would have been a better idea than waiting for a confirmation.

      We also find, that as we increase the length of our moving average, we also decrease the need to wait for a confirmation, and can instead take slightly more aggressive entries.

      That’s what backtesting is all about. You can use it to find optimal settings for your trading strategy, and discover insights using concrete data that were hidden in plain sight.

      There are other, similar examples towards the end of the tutorial, which you may find fascinating to review as well (I know I do!).

      You can also take this one step further, and use our Moving Average Crossover Backtester (also free), to find early indications of a new trend forming.

       

      Switch Statement – thinkScript Example

      The switch statement is a simple, yet robust way to define one variable in multiple different ways, depending on each “scenario” in your trading strategy.

      The best way to learn and use switch statements is through trial and error, and practice writing the actual code (the syntax is odd, and takes a bit of ‘warming up’ to get used to it).

       

      Example #1: Change the Entry Moving Average Based on User Selection

      We’d like for the user to be able to select which moving average they’d like to use, for the pullback entry. This same “text-only” value needs to be connected with the actual moving averages inside of the code, that hold numerical values.

      Here’s how we can define our input statement, to let the user select from a variety of limited choices:

      input entryMA = {default EMA8, EMA21, EMA34, SMA50, SMA200};

      With just that code, the user sees a drop-down menu, when they double-click the strategy:

      Moving Average Pullback Backtester Choices

      The next part is defining what those different switch conditions actually change, within the code. That’s where using switch and case statements becomes useful.

      Here is how we can connect the input variable to the selected moving average, within the code:

      def EMA8 = ExpAverage(close, 8);
      def EMA21 = ExpAverage(close, 21);
      def EMA34 = ExpAverage(close, 34);
      def SMA50 = SimpleMovingAvg(close, 50);
      def SMA200 = SimpleMovingAvg(close, 200);
      
      def entryPullbackMA;
      switch (entryMA){
      case EMA8:
          entryPullbackMA = EMA8;
      case EMA21:
          entryPullbackMA = EMA21;
      case EMA34:
          entryPullbackMA = EMA34;
      case SMA50:
          entryPullbackMA = SMA50;
      case SMA200:
          entryPullbackMA = SMA200;
      }

      As you should notice, each different user selection that we added in the input variable needs to be defined as a separate ‘case’, which contains a final value for the entryPullbackMA variable.

       

      Example #2: Change the Entry Signal and Entry Price Based on User Selection

      The user can select between one of two different moving average pullback strategies:

      1. Option #1: Enter blindly, using only the pullback as our entry signal, without waiting for price action to confirm the move. This means that the backtester would be entering any time price touches the selected moving average, as long as the overall 5 moving averages are stacked, in the direction of the trend.
      2. Option #2: Wait for price action to confirm the pullback using a simple reversal candlestick pattern, and enter when we have a close above the previous bar’s high (for long entries), or below the previous bar’s low (for short entries).

      Here is how we define that, within the code, using switch and case statements:

      def bullEntrySignal;
      def bullEntryPrice;
      def bearEntrySignal;
      def bearEntryPrice;
      
      switch(entryType){
      case pullbackOnly:
          bullEntrySignal = bullishStackedMAs and low <= entryPullbackMA; bullEntryPrice = entryPullbackMA; bearEntrySignal = bearishStackedMAs and high >= entryPullbackMA;
          bearEntryPrice = entryPullbackMA;
      case Confirmation:
          bullEntrySignal = bullishStackedMAs and Lowest(low,2) <= entryPullbackMA and close > high[1]; 
          bullEntryPrice = close;
          bearEntrySignal = bearishStackedMAs and Highest(high,2) >= entryPullbackMA and close < low[1]; 
          bearEntryPrice = close;
      }
      

      The bullish entrySignal variable changes, depending on if the user wants the backtester to enter blindly (Option 1), or wait for a confirmation of price action (Option2 ).

      Here is a link to the ThinkOrSwim documentation, which details switch and case statements a bit more.

      If you’d like to practice, you can try defining other entry and exit strategies, that all center around using moving averages and finding pullback trades (example list from Investopedia).

      downloads

      Download the Moving Average Pullback Backtester for ThinkorSwim.

      The download contains a STRATEGY.ts file, which you can directly import into your ThinkOrSwim platform.

      Download Backtester

      Download the Moving Average Pullback Backtester for ThinkorSwim.

      The download contains a STRATEGY.ts file, which you can directly import into your ThinkOrSwim platform.

      Have your own idea?

      Let us help you turn your trading strategy into a powerful indicator, scan and backtester.