# Rate of Change Indicator for ThinkorSwim With RSI Signals

> Free rate of change indicator for ThinkorSwim: a 14-bar ROC with highest, lowest and halfway lines, plus arrows when it turns with an RSI breakout.

Page: https://tosindicators.com/indicators/rate-of-change  
Video: https://www.youtube.com/watch?v=jBDuTkSLSAs  
Section: Indicators  
Skill: Beginner  
Access: free  
Updated: 2026-10-17

In this tutorial I build a rate of change indicator for ThinkorSwim, starting from the built-in Rate of Change study. I turn the ROC into a range-bound oscillator: one line at its highest value on the chart, one at its lowest, and a dashed line halfway to each, so you can see when the ROC is in an extreme zone.

Then I add a green arrow when the ROC turns up in the lower zone on the same bar the built-in RSI fires its bullish breakout signal, and a red arrow for the bearish side. The video is unrehearsed, bugs and chart bubble tests included, so it doubles as a lesson in turning an idea into thinkScript for anyone who wants to make a built-in study smarter.

## Key Takeaways

- Our Rate of Change indicator starts from ThinkorSwim's built-in ROC study: the percent change between the close and the close 14 bars ago.

- It draws the highest and lowest ROC on the chart as solid red and green lines, using HighestAll and LowestAll, and the halfway point between each line and zero as a dashed line.

- The upper extreme zone is the space between the upper dashed line and the red line. The lower extreme zone is the space between the lower dashed line and the green line.

- A green up arrow plots when the ROC is in the lower zone, higher than on the previous bar, and the RSI crosses above its oversold line on the same bar. The red down arrow mirrors it in the upper zone with the RSI crossing below overbought.

- I built it on a daily Amazon chart and checked it on a 5-minute chart. Our rate of change scans turn the same code into 4 ThinkorSwim scans.

## What Is the Rate of Change Indicator?

The rate of change (ROC) indicator measures how far price has moved, in percent, over a set number of bars. The textbook formula is the close minus the close n bars ago, divided by the close n bars ago, times 100. A reading of 5 means price is 5% higher than n bars ago, and -5 means it is 5% lower.

ThinkorSwim's built-in Rate of Change study uses 14 bars by default and writes the same formula as `(price / price[length] - 1) * 100`. It plots the ROC around a zero line and shades it from light red near its recent lows to yellow near its recent highs.

The ROC has no fixed upper limit. The RSI stays between 0 and 100, but a ROC reading that is extreme on one chart can be ordinary on another, so the raw number doesn't tell you whether a reading is extreme for that chart. That's the gap this indicator fills.

## What Our Rate of Change Indicator Plots

Our rate of change indicator keeps the built-in ROC line and zero line. The table shows the 4 lines and 2 arrows it adds:

| Plot | Code | Look | What it marks |
| --- | --- | --- | --- |

| highestROC | HighestAll(ROC) | Solid red, weight 3 | The highest ROC on the chart |

| lowestROC | LowestAll(ROC) | Solid green, weight 3 | The lowest ROC on the chart |

| midPtHighestROC | highestROC/2 | Dashed light red, weight 1 | The start of the upper extreme zone |

| midPtLowestROC | lowestROC/2 | Dashed light green, weight 1 | The start of the lower extreme zone |

| bullSignal | ROC turns up in the lower zone and the RSI fires UpSignal | Green up arrow, weight 3 | Bullish signal |

| bearSignal | ROC turns down in the upper zone and the RSI fires DownSignal | Red down arrow, weight 3 | Bearish signal |

## The Idea: An Extreme ROC Plus an RSI Breakout

The rate of change idea started on a daily chart of Amazon, with the built-in ROC in the first lower panel and the built-in RSI in the second.

The plan had 2 parts. First, turn the ROC into a range-bound oscillator by marking its high and low, then split the distance from zero to each in half and call the outer halves extreme. Second, flag the bars where the RSI gives a breakout signal while the ROC sits in an extreme zone.

I marked 4 examples on that Amazon chart before writing any code:

- **Bullish.** The RSI gave its breakout signal while the ROC was in the lower extreme zone, and price rallied from there.

- **Bearish.** The RSI gave a bearish breakout signal with the ROC in the upper extreme zone, and price pulled back a little.

- **Bullish.** The same setup on the lower side, followed by a small bounce.

- **Bullish.** During an Amazon relief rally shortly before the video, the ROC was well inside the lower extreme zone when the RSI signals came.

In plain terms, a bullish signal says the ROC is slowing down. It is moving from a negative reading toward a positive one, or at least falling less quickly, while the RSI says price is moving up out of oversold territory.

Before coding, I said up front that I had no idea whether this indicator would give an edge. The video is about the process of turning an idea into code, and I wanted it to work on any timeframe.

## How to Add the Rate of Change Indicator to ThinkorSwim

The rate of change indicator installs as one study in a lower panel:

- Download the zip from this page. Inside is one study file, `TI_ROCSTUDY.ts`.

- Import it as a study. Our guide on [how to import a study into ThinkorSwim](https://tosindicators.com/thinkscript/how-to-import-thinkorswim) walks through each click.

- Add it to a chart. It opens in a lower panel.

- To build it yourself, click the Studies icon and search for the built-in Rate of Change study. Click the scroll icon with the lock next to it, copy all of its code, then click **Create**, delete anything in the code field and paste. I named mine TI_ROC.

## How the Rate of Change Code Works

The rate of change code adds range lines, extreme zones and signals to ThinkorSwim's built-in ROC, one block at a time. I tested each block before moving to the next, often with a chart bubble, which is a good habit when you build your own studies.

### Step 1: The Range Lines

```
plot highestROC = HighestAll(ROC);
plot lowestROC = LowestAll(ROC);
```

HighestAll and LowestAll return the highest and lowest ROC across every bar loaded on the chart. They give the ROC a top and a bottom, so it reads like a range-bound oscillator.

### Step 2: The Halfway Lines

```
plot midPtHighestROC = highestROC/2;
plot midPtLowestROC = lowestROC/2;
```

Each dashed line sits halfway between zero and the highest or lowest ROC. The full formula is the highest ROC minus 0, divided by 2, so the code skips the minus zero. The space between a dashed line and its solid line is the extreme zone.

### Step 3: Formatting

```
highestROC.setLineWeight(3);
highestROC.setDefaultColor(Color.Red);
midPtHighestROC.setLineWeight(1);
midPtHighestROC.setStyle(Curve.Short_Dash);
midPtHighestROC.setDefaultColor(Color.light_red);
```

The outer lines are red and green at weight 3, and the halfway lines light red and light green, dashed, at weight 1. I set the colors in code so they stay the same on any platform setup.

### Step 4: The Extreme Zones

```
def extremeHigh = ROC < HighestROC and ROC >= midPtHighestROC;
def extremeLow = ROC > LowestROC and ROC  ROC[1]);
def ROCDownSignal = extremeHigh and (ROC < ROC[1]);
```

ROCUpSignal is true when the ROC is in the lower extreme zone and higher than on the previous bar, so it has stopped falling and started to turn. ROCDownSignal is the mirror in the upper zone. While testing, I plotted each one as arrows on its own to confirm it.

### Step 7: The Final Arrows

```
plot bullSignal = if ROCUpSignal and RSIUpSignal then ROC else double.nan;
plot bearSignal = if ROCDownSignal and RSIDownSignal then ROC else double.nan;
bullSignal.setPaintingStrategy(PaintingStrategy.Arrow_Up);
```

A signal needs both conditions on the same bar. The `then ROC else double.nan` part puts the arrow on the ROC line. Without it the arrows sit on the zero line, which is what happened on my first try.

## Rate of Change Settings

The rate of change indicator has 3 inputs. Everything else sits in the code.

| Setting | Default | What it does |
| --- | --- | --- |

| length | 14 | How many bars back the ROC compares the close with. |

| colorNormLength | 14 | Bars used to shade the ROC line from light red to yellow. Colors only. |

| price | close | The price the ROC measures. |

| Halfway divisor (in the code) | 2 | Where the dashed lines sit between zero and the outer lines. |

| RSI settings (in the code) | Built-in defaults | `RSI()` with empty parentheses uses the built-in RSI's own inputs, including its overbought and oversold lines. |

Dividing by a larger number than 2 moves the dashed lines toward zero, so more bars count as extreme and more bars can produce arrows. In the video I mentioned a sensitivity factor as one way to improve the indicator.

One way to add it is to replace that 2 with an input. This edit is my suggestion and is not in the video or the download. Put the input line under the other inputs and replace the two midpoint lines:

```
input sensitivity = 2;
plot midPtHighestROC = highestROC / sensitivity;
plot midPtLowestROC = lowestROC / sensitivity;
```

With sensitivity at 2 the indicator plots exactly as the download does. Keep it above 1: at 1 or below, the dashed lines reach or pass the solid ones and the extreme zones disappear.

## Why the Lines Depend on Your Chart's History

HighestAll and LowestAll read every bar loaded on the chart, so the range lines depend on your chart's time range. A 1-year daily chart and a 5-year daily chart of the same stock can draw different red and green lines, and so different extreme zones and arrows.

The lines also move when the ROC sets a new high or low, which can change which past bars count as extreme. Before you judge an old arrow, check the chart's time range. Because extremeHigh needs the ROC below the red line, the one bar that sets the highest ROC never counts as extreme, and the same goes for the lowest.

Our [rate of change scans](https://tosindicators.com/scans/roc) show the same effect in the Scan tab. A 30-minute bearish signal on Apple appeared on the chart only after I cut the chart's history from 90 days to 15 days.

## What the Indicator Showed in the Video

The finished rate of change indicator plotted fewer arrows on the daily Amazon chart than my 4 planning examples. Two sets of signals showed up, both while the ROC was still in an extreme zone. My guess in the video was that in the earlier examples, the RSI signal and the ROC turn didn't line up on the same bar.

On a 5-minute chart, the arrows plotted the way I expected. I loaded the RSI alongside to confirm each green arrow came with an RSI breakout signal and a rising ROC. At the end I pointed out that you can switch to another market, such as the S&P futures, and the indicator should keep plotting the same way.

One turn the indicator missed was a bearish one that came after the extreme edge had already been crossed. To catch it, the code would need context: remember that the extreme edge was crossed, then accept a turn lower just below it. I left that for a separate tutorial.

## How to Read the Rate of Change Arrows

A rate of change arrow marks momentum turning at an extreme on the same bar the RSI breaks out. A bullish arrow says the ROC has stopped falling inside the lower zone while the RSI climbs out of oversold. In the planning examples, what followed ranged from a rally to a small bounce or a slight pullback, so not every arrow leads to a big move.

Treat an arrow as a read on momentum and check it against price levels before acting. Decide where your stop goes first, and size the position so a stop-out costs a small, fixed share of your account.

For more ways to work with momentum, our [Smarter MACD](https://tosindicators.com/indicators/smarter-macd) starts from the built-in MACD code the same way, and if a change of your own throws an error, our guide on [how to fix bugs in thinkScript code](https://tosindicators.com/thinkscript/fix-bugs) can help you track it down.

## thinkScript code

```
#
# TD Ameritrade IP Company, Inc. (c) 2007-2022
#

# Modified by TOS Indicators
#Home of the Volatility Box
#Indicator: Rate of Change x RSI
#Code written in 2022
#Full Tutorial: https://www.tosindicators.com/indicators/rate-of-change

declare lower;

input length = 14;
input colorNormLength = 14;
input price = close;

assert(length > 0, "'length' must be positive: " + length);

plot ROC = if price[length] != 0 then (price / price[length] - 1) * 100 else 0;
plot ZeroLine = 0;

ROC.DefineColor("Highest", Color.YELLOW);
ROC.DefineColor("Lowest", Color.LIGHT_RED);
ROC.AssignNormGradientColor(colorNormLength, ROC.color("Lowest"), ROC.color("Highest"));
ZeroLine.SetDefaultColor(GetColor(5));

plot highestROC = HighestAll(ROC);
plot lowestROC = LowestAll(ROC);

plot midPtHighestROC = highestROC/2;
plot midPtLowestROC = lowestROC/2;

highestROC.setLineWeight(3);
lowestROC.setLineWeight(3);
highestROC.setDefaultColor(Color.Red);
lowestROC.setDefaultColor(Color.Green);

midPtHighestROC.setLineWeight(1);
midPtLowestROC.setLineWeight(1);
midPtHighestROC.setStyle(Curve.Short_Dash);
midPtLowestROC.setStyle(Curve.Short_Dash);
midPtHighestROC.setDefaultColor(Color.light_red);
midPtLowestROC.setDefaultColor(Color.light_green);

def extremeHigh = ROC < HighestROC and ROC >= midPtHighestROC;
def extremeLow = ROC > LowestROC and ROC <= midPtLowestROC;

def RSIUpSignal = RSI().upSignal;
def RSIDownSignal = RSI().downSignal;

def ROCUpSignal = extremeLow and (ROC > ROC[1]);
def ROCDownSignal = extremeHigh and (ROC < ROC[1]);

plot bullSignal = if ROCUpSignal and RSIUpSignal then ROC else double.nan;
plot bearSignal = if ROCDownSignal and RSIDownSignal then ROC else double.nan;

bullSignal.setPaintingStrategy(PaintingStrategy.Arrow_Up);
bullSignal.setDefaultColor(Color.Green);
bullSignal.setLineWeight(3);

bearSignal.setPaintingStrategy(PaintingStrategy.Arrow_Down);
bearSignal.setDefaultColor(Color.Red);
bearSignal.setLineWeight(3);
#AddChartBubble(RSIUpSignal, ROC, RSIUpSignal, color.yellow);
```

## Questions

### What is the rate of change indicator?

The rate of change (ROC) indicator measures the percent change in price over a set number of bars: the close minus the close n bars ago, divided by the close n bars ago, times 100. ThinkorSwim's built-in study uses 14 bars. A positive reading means price is higher than 14 bars ago, and a negative reading means it is lower.

### How is this ROC indicator different from the built-in one?

It keeps the built-in ROC line and adds lines at the highest and lowest ROC on the chart, plus dashed lines halfway to each, which mark the extreme zones. It also plots a green or red arrow when the ROC turns inside an extreme zone on the same bar the built-in RSI fires its breakout signal.

### How are the rate of change extreme zones calculated?

The upper zone runs from half the highest ROC on the chart up to the highest ROC. The lower zone runs from half the lowest ROC down to the lowest ROC. HighestAll and LowestAll find those values across every bar loaded on the chart, and the code divides each by 2 for the dashed lines.

### What triggers a bullish arrow on the rate of change indicator?

Three things on the same bar: the ROC is in the lower extreme zone, the ROC is higher than on the previous bar, and the built-in RSI fires its UpSignal by crossing above its oversold line. The bearish arrow needs the ROC in the upper zone, lower than on the previous bar, and the RSI crossing below its overbought line.

### Why do the lines move when I change the chart's time range?

The highest and lowest lines come from HighestAll and LowestAll, which read every bar on the chart. Load more or less history and the highest and lowest ROC can change, which moves the dashed lines, the extreme zones and the arrows. Compare charts that load the same time range.

### Does the rate of change indicator work on intraday charts?

Yes. I built it on a daily Amazon chart, then switched to a 5-minute chart, and the arrows plotted the way I expected, lining up with the RSI's breakout signals. The code has no timeframe setting, so it reads whatever chart you put it on. At the end of the video I said it should plot the same way on other markets, such as the S&P futures.

### How do I change the ROC length?

Open the study's settings and change length, which defaults to 14. It sets how many bars back the ROC compares the close with. colorNormLength, also 14, only changes the line's color shading. The RSI part uses the built-in RSI's own settings, so changing length doesn't change the RSI signals.

### Can I scan for the rate of change signals?

Yes. Our rate of change scans tutorial pastes this indicator's code into a ThinkorSwim scan filter and comes with 4 ready-made scans: bullish signal, bearish signal, lower extreme zone and upper extreme zone. A scan filter takes exactly one plot, so every other plot becomes a def.

## Related

- [How to Build a Rate of Change Scan in ThinkorSwim](https://tosindicators.com/scans/roc)
- [How to Build a Fair Value Gap Indicator in ThinkorSwim](https://tosindicators.com/indicators/fair-value-gap)
- [How to Build a Heiken Ashi Trend Dots Indicator in ThinkorSwim](https://tosindicators.com/indicators/heiken-ashi)
- [How to Build a Commodities Tracker in ThinkorSwim](https://tosindicators.com/indicators/commodities-tracker)
- [Trend Squeeze Indicator for ThinkorSwim](https://tosindicators.com/indicators/trend-squeeze)
- [Volume Intensity Indicator for ThinkorSwim](https://tosindicators.com/indicators/volume-intensity)

