# Dynamic RSI

> Build adaptive RSI indicator that adjusts to changing price conditions. Enhanced overbought/oversold detection for ThinkOrSwim.

Page: https://tosindicators.com/indicators/dynamic-rsi  
Video: https://www.youtube.com/watch?v=5zB-d-NWinE  
Section: Indicators  
Skill: Intermediate  
Access: free  
Updated: 2026-02-12

## Introduction


In this tutorial, I'll show you how to build a Dynamic RSI Indicator using thinkScript code for the ThinkOrSwim (TOS) trading platform. We’ll leverage code snippets, and our own logic, to create a powerful RSI indicator that adapts to market volatility.


- RSI with Bollinger Bands for dynamic overbought/oversold levels

- Automatic trend line detection for objective support and resistance

- Squeeze detection for compression and breakout identification

- Dynamic yellow average line for trend confirmation

- Upper and lower study configuration for comprehensive analysis




The Dynamic RSI indicator combines Bollinger Bands, trend lines, and various RSI metrics. The Bollinger Bands on the RSI chart help gauge overbought and oversold conditions, while trend lines provide support and resistance levels for potential trading signals.

I'll take you through, step-by-step, how to build the Dynamic RSI indicator for ThinkOrSwim below.

## The Complete Guide to Building a Dynamic RSI Indicator in ThinkOrSwim


The Relative Strength Index (RSI) is one of the most widely used technical indicators, but in its standard form, it often generates too many false signals and lacks the nuanced analysis needed for today's complex markets. The Dynamic RSI indicator changes that by layering additional analytical components that transform the basic RSI into a sophisticated trading system capable of identifying institutional activity and market reversals with remarkable precision.


This tutorial takes you through the complete process of building Keith's Dynamic RSI indicator, a system that has been refined through thousands of hours of real trading and market observation. Unlike static indicators that use fixed parameters, this Dynamic RSI adapts to market conditions, providing more reliable signals and reducing false breakouts that plague traditional RSI analysis.



## Understanding the Core Components


The Dynamic RSI system consists of five integrated components that work together to create a comprehensive trading tool. Each component serves a specific purpose and contributes unique information to your trading decisions.


The foundation starts with the traditional RSI calculation, but we enhance it with a dynamic yellow average line that provides smoother trend identification. This yellow line acts as a filter, helping distinguish between temporary price fluctuations and meaningful trend changes. When this dynamic average crosses above or below traditional overbought and oversold levels, it signals that institutional money is actively moving the market.


Bollinger Bands applied to the RSI create adaptive overbought and oversold zones that adjust to market volatility. Unlike fixed RSI levels at 70 and 30, these Bollinger Bands expand during volatile periods and contract during quiet markets, providing more accurate reversal signals. When the RSI breaks outside these bands, it indicates extreme conditions that often precede significant price movements.


Automatic trend line detection removes the subjectivity from traditional trend line analysis. Using Mobius's proven algorithm, the indicator automatically draws trend lines on the RSI, identifying support and resistance levels that would be impossible to spot manually in real-time. These trend lines provide objective entry and exit points based on mathematical calculations rather than subjective interpretation.


Squeeze detection monitors the compression between the upper and lower Bollinger Bands on the RSI. When these bands compress, it indicates a period of low volatility that often precedes explosive price movements. The squeeze dots provide early warning of impending breakouts, allowing you to position before the crowd recognizes the setup.



## Signal Generation and Filtering Logic


The Dynamic RSI generates multiple types of signals, each serving different trading objectives. Understanding how to interpret and combine these signals is crucial for successful implementation.


Bollinger Band breakthrough signals occur when the RSI breaks above the upper band or below the lower band. These signals indicate extreme momentum conditions that often lead to price reversals. For bullish signals, the system requires the RSI to fall below the lower Bollinger Band while ensuring the lower band itself is below the 45 level and the RSI is in oversold territory below 30. This multi-condition approach significantly reduces false signals.


Bearish signals require the RSI to break above the upper Bollinger Band while the upper band is above 55 and the RSI is in overbought territory above 70. These additional filters ensure that signals only generate during meaningful extreme conditions rather than temporary fluctuations.


Dynamic average crossover signals provide trend confirmation and reversal warnings. When the yellow dynamic average crosses above the overbought level (70), it indicates the market is entering a potentially unstable bullish phase. The system also tracks when the dynamic average exits these extreme zones, providing valuable information about trend sustainability.



## Step-by-Step Coding Tutorial


Now let's walk through the complete process of building this Dynamic RSI indicator from the ground up. By building it step-by-step, you'll understand exactly how the indicator functions and be able to customize it for your specific needs.



### Step 1: Setting Up Global Variables and Input Parameters


We start by declaring this as a lower study and setting up all the input parameters that control the indicator's behavior:



```
declare lower;
input length = 14;
input over_Bought = 70;
input over_Sold = 30;
input price = close;
input averageType = AverageType.WILDERS;
input showBreakoutSignals = no;
input Volatility_Band = 14;
input STDEV_Multiplier = 2;
input RSI_Period = 14;
input RSI_Price_Line = 2;
```


The `declare lower` statement tells ThinkOrSwim to display this indicator in the lower study area. The input variables provide flexibility for different trading styles - day traders might use shorter periods while swing traders prefer longer settings.



### Step 2: Building the Core RSI Calculations


Next, we calculate the RSI using a more flexible approach that allows different moving average types:



```
def NetChgAvg = MovingAverage(averageType, price - price[1], length);
def TotChgAvg = MovingAverage(averageType, AbsValue(price - price[1]), length);
def ChgRatio = if TotChgAvg != 0 then NetChgAvg / TotChgAvg else 0;

def DYNRSI = reference RSI(RSI_Period);
def Price1 = if averageType == AverageType.SIMPLE then Average(DYNRSI, RSI_Price_Line) else ExpAverage(DYNRSI, RSI_Price_Line);
def SDBB = StDev(DYNRSI, Volatility_Band);
```


These calculations create the foundation for our dynamic RSI. The `DYNRSI` references the standard RSI calculation, while `SDBB` calculates the standard deviation for our Bollinger Bands.



### Step 3: Plotting RSI and Reference Levels


Now we plot the actual RSI line along with reference levels and the crucial dynamic yellow average line:



```
plot RSI = 50 * (ChgRatio + 1);
plot MiddleLine = 50;
plot MiddleLine1 = 70;
plot MiddleLine2 = 30;

plot DYNAverage = Average(DYNRSI, Volatility_Band);
DYNAverage.SetDefaultColor(Color.YELLOW);
DYNAverage.SetLineWeight(2);
```


The yellow dynamic average line is crucial - it smooths out RSI noise and provides more reliable trend signals than the raw RSI line.



### Step 4: Creating Dynamic Bollinger Bands


The Bollinger Bands create adaptive overbought and oversold zones that adjust to market volatility:



```
plot UpperBollinger = DYNAverage + STDEV_Multiplier * SDBB;
UpperBollinger.SetDefaultColor(Color.RED);
plot LowerBollinger = DYNAverage - STDEV_Multiplier * SDBB;
LowerBollinger.SetDefaultColor(Color.GREEN);
```


These bands use the dynamic average as the middle line rather than a simple moving average. When the RSI breaks outside these bands, it indicates extreme conditions.



### Step 5: Adding Signal Generation Logic


We add breakthrough signal logic with multiple filters to reduce false positives:



```
plot BullSignal = if RSI = 70 
                  then 1 else 0;

BullSignal.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
BearSignal.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
```


The bull signal requires the RSI to break below the lower Bollinger Band while ensuring we're in truly oversold conditions. These multiple filters dramatically reduce false signals.



### Step 6: Implementing Dynamic Average Entry/Exit Tracking


One of Keith's key insights was tracking when the dynamic average enters and exits extreme zones:



```
def SuperOverbought = DYNAverage crosses above over_Bought;
def SuperOversold = DYNAverage crosses below over_Sold;
def SuperOverboughtExit = DYNAverage crosses below over_Bought;
def SuperOversoldExit = DYNAverage crosses above over_Sold;

AddChartBubble(SuperOverbought and !SuperOverbought[1], close, "Enter Super OB", Color.LIGHT_RED);
AddChartBubble(SuperOversold and !SuperOversold[1], close, "Enter Super OS", Color.LIGHT_GREEN);
AddChartBubble(SuperOverboughtExit and !SuperOverboughtExit[1], close, "Exit Super OB", Color.RED);
AddChartBubble(SuperOversoldExit and !SuperOversoldExit[1], close, "Exit Super OS", Color.GREEN);
```


These chart bubbles provide context about market momentum and potential trend changes, with color coding to help you quickly identify signal types.



### Step 7: Adding Squeeze Detection


The squeeze detection monitors when the Bollinger Bands compress, indicating low volatility that often precedes explosive moves:



```
def BBDistance = if UpperBollinger - LowerBollinger
```


When the distance between the Bollinger Bands falls below 10 points, red dots appear warning of impending volatility expansion.



### Step 8: Incorporating Automatic Trend Lines


The automatic trend line system uses Mobius's proven inertia-based algorithm:



```
input TrendLineLength1 = 150;
input TrendLineLength2 = 80;
input TrendLineLength3 = 40;

def Inertia1 = InertiaAll(RSI, TrendLineLength1);
def Inertia2 = InertiaAll(RSI, TrendLineLength2);
def Inertia3 = InertiaAll(RSI, TrendLineLength3);

def TL_Bull2 = Inertia2 - (HighestAll(AbsValue(Inertia2 - RSI)) * 0.8);
def TL_Bear2 = Inertia2 + (HighestAll(AbsValue(Inertia2 - RSI)) * 0.8);

plot TrendLine2a = TL_Bull2;
plot TrendLine2b = TL_Bear2;
```


This creates multiple trend lines with different sensitivities. We focus on the medium-length trend lines as they provide the best balance between reliability and signal frequency.



### Step 9: Adding Trend Line Signal Detection


Finally, we add logic to detect when the RSI bounces off or breaks through the trend lines:



```
def trendLineSignalLong = if RSI >= TrendLine2b and RSI[1] < TrendLine2b then 1 else 0;
def trendLineSignalShort = if RSI  TrendLine2a then 1 else 0;

AddChartBubble(trendLineSignalLong and !trendLineSignalLong[1], TrendLine2b, "Long", Color.LIGHT_GREEN);
AddChartBubble(trendLineSignalShort and !trendLineSignalShort[1], TrendLine2a, "Short", Color.LIGHT_RED);
```


These chart bubbles appear when the RSI touches the trend lines, providing clear entry signals for trend line bounces.



### Step 10: Creating the Upper Study Version


For the upper study that appears on your price chart, we simplify the display to show only the most critical signals:



```
declare upper;

# Copy the same calculation logic here but change plots to definitions

plot BullishArrow = if (RSI breakthrough conditions met) then close else Double.NaN;
plot BearishArrow = if (RSI breakthrough conditions met) then close else Double.NaN;
plot SqueezeUpper = if BBDistance then close else Double.NaN;

BullishArrow.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
BearishArrow.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
SqueezeUpper.SetPaintingStrategy(PaintingStrategy.POINTS);
```


The upper study focuses on actionable signals without cluttering your price chart. Arrows appear when breakthrough conditions are met, and squeeze dots warn of volatility compression.



## Customization and Integration


Once you have the basic indicator built, you can customize it for your specific trading style. For day trading, consider reducing the RSI period to 8-10 and using shorter Bollinger Band periods. Swing traders typically prefer longer periods that filter out noise.


The Dynamic RSI works exceptionally well as a confirmation tool with other trading systems. When combined with volatility-based systems like the Volatility Box, Dynamic RSI signals help validate trading opportunities and improve entry timing. Keith's research suggests that 4-minute charts provide an optimal balance between signal frequency and accuracy for day trading.


The Dynamic RSI represents a significant evolution beyond traditional RSI analysis. By combining multiple analytical layers, it creates a comprehensive trading system that adapts to changing market conditions. Start by implementing the basic version and familiarize yourself with the signals it generates before experimenting with customization options to optimize it for your trading style.

## thinkScript code

```
### Includes code written by Keith C., Mobius, and TOS Indicators 
### Last Update: 11/3/2019 
### Full Tutorial Available Here: https://www.tosindicators.com/indicators/dynamic-rsi

### Global Variables ###
declare lower;
input length = 14;
input over_Bought = 70;
input over_Sold = 30;
input price = close;
input averageType = AverageType.WILDERS;
input showBreakoutSignals = no;
input Volatility_Band = 14; #20-40
input STDEV_Multiplier = 2;
input RSI_Period = 14; #8-25
input RSI_Price = 0; #0-6
input RSI_Price_Line = 2;


### Keith RSI, BB, DYN ###
def NetChgAvg = MovingAverage(averageType, price - price[1], length);
def TotChgAvg = MovingAverage(averageType, AbsValue(price - price[1]), length);
def ChgRatio = if TotChgAvg != 0 then NetChgAvg / TotChgAvg else 0;

def DYNRSI = reference RSI(RSI_Period);
def Price1 = if averageType == AverageType.SIMPLE then Average(DYNRSI, RSI_Price_Line) else ExpAverage(DYNRSI, RSI_Price_Line);
def SDBB = StDev(DYNRSI, Volatility_Band);

plot RSI = 50 * (ChgRatio + 1);
plot MiddleLine = 50;
plot MiddleLine1 = 70;
plot MiddleLine2 = 30;

plot DYNAverage = Average(DYNRSI, Volatility_Band);
DYNAverage.SetDefaultColor(Color.YELLOW);
DYNAverage.SetLineWeight(2);

plot UpperBollinger = DYNAverage + STDEV_Multiplier * SDBB;
UpperBollinger.SetDefaultColor(Color.RED);
plot LowerBollinger = DYNAverage - STDEV_Multiplier * SDBB;
LowerBollinger.SetDefaultColor(Color.GREEN);

plot OverSold = over_Sold;
plot OverBought = over_Bought;
plot UpSignal = if RSI crosses above OverSold then OverSold else Double.NaN;
plot DownSignal = if RSI crosses below OverBought then OverBought else Double.NaN;

UpSignal.SetHiding(!showBreakoutSignals);
DownSignal.SetHiding(!showBreakoutSignals);

RSI.DefineColor("OverBought", GetColor(5));
RSI.DefineColor("Normal", GetColor(9));
RSI.DefineColor("OverSold", GetColor(6));
RSI.AssignValueColor(if RSI > over_Bought then RSI.Color("OverBought") else if RSI < over_Sold then RSI.Color("OverSold") else RSI.Color("Normal"));
MiddleLine.SetDefaultColor(GetColor(4));
MiddleLine1.SetDefaultColor(GetColor(5));
MiddleLine2.SetDefaultColor(GetColor(6));
UpSignal.SetDefaultColor(Color.UPTICK);
UpSignal.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
DownSignal.SetDefaultColor(Color.DOWNTICK);
DownSignal.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);

AddCloud(RSI, OverBought, Color.RED, Color.CURRENT);
AddCloud(OverSold, RSI, Color.GREEN, Color.CURRENT);



### Squeeze Relationship (Lower and Upper Bollinger Bands Compressing)
def BBDistance = if UpperBollinger - LowerBollinger  TL_Bull1[1];
def slope1b = TL_Bear1 > TL_Bear1[1];

def TL_Bull2 = Inertia2 - (HighestAll(AbsValue(Inertia2 - RSI)) * 0.8);
def TL_Bear2 = Inertia2 + (HighestAll(AbsValue(Inertia2 - RSI)) * 0.8);
def slope2a = TL_Bull2 > TL_Bull2[1];
def slope2b = TL_Bear2 > TL_Bear2[1];

def TL_Bull3 = Inertia3 - (HighestAll(AbsValue(Inertia3 - RSI)) * 0.8);
def TL_Bear3 = Inertia3 + (HighestAll(AbsValue(Inertia3 - RSI)) * 0.8);
def slope3a = TL_Bull3 > TL_Bull3[1];
def slope3b = TL_Bear3 > TL_Bear3[1];
#Long length
plot TrendLine1a = if slope1a > 0 then TL_Bull1 else TL_Bear1;
TrendLine1a.SetStyle(Curve.FIRM);
TrendLine1a.SetLineWeight(1);
TrendLine1a.AssignValueColor(if slope1a and IsAscending(RSI, 10) then Color.WHITE else if slope1a then Color.WHITE else if !IsAscending(RSI, 10) then Color.WHITE else Color.WHITE);

plot TrendLine1b = if slope1b > 0 then TL_Bear1 else TL_Bull1;
TrendLine1b.SetStyle(Curve.FIRM);
TrendLine1b.SetLineWeight(1);
TrendLine1b.AssignValueColor(if slope1b and IsAscending(RSI, 10) then Color.WHITE else if slope1b then Color.WHITE else if !IsAscending(RSI, 10) then Color.WHITE else Color.WHITE);
#Medium length
plot TrendLine2a = if slope2a > 0 then TL_Bull2 else TL_Bear2;
TrendLine2a.SetStyle(Curve.FIRM);
TrendLine2a.SetLineWeight(1);
TrendLine2a.AssignValueColor(if slope2a and IsAscending(RSI, 10) then Color.LIGHT_GREEN else if slope2a then Color.LIGHT_GREEN else if !IsAscending(RSI, 10) then Color.MAGENTA else Color.MAGENTA);

plot TrendLine2b = if slope2b > 0 then TL_Bear2 else TL_Bull2;
TrendLine2b.SetStyle(Curve.FIRM);
TrendLine2b.SetLineWeight(1);
TrendLine2b.AssignValueColor(if slope2b and IsAscending(RSI, 10) then Color.LIGHT_GREEN else if slope2b then Color.LIGHT_GREEN else if !IsAscending(RSI, 10) then Color.MAGENTA else Color.MAGENTA);
#Short length
plot TrendLine3a = if slope3a > 0 then TL_Bull3 else TL_Bear3;
TrendLine3a.SetStyle(Curve.FIRM);
TrendLine3a.SetLineWeight(1);
TrendLine3a.AssignValueColor(if slope3a and IsAscending(RSI, 10) then Color.LIGHT_GREEN else if slope3a then Color.LIGHT_GREEN else if !IsAscending(RSI, 10) then Color.PINK else Color.PINK);

plot TrendLine3b = if slope3b > 0 then TL_Bear3 else TL_Bull3;
TrendLine3b.SetStyle(Curve.FIRM);
TrendLine3b.SetLineWeight(1);
TrendLine3b.AssignValueColor(if slope3b and IsAscending(RSI, 10) then Color.LIGHT_GREEN else if slope3b then Color.LIGHT_GREEN else if !IsAscending(RSI, 10) then Color.PINK else Color.PINK);

def trendLineSignalLong = if RSI >= TrendLine2B and RSI[1] < TrendLine2B then 1 else 0;
def trendLineSignalShort = if RSI  TrendLine2A then 1 else 0;

AddChartBubble(trendLineSignalLong and !trendLineSignalLong[1], trendLine2B, "Short", Color.Light_Red);
AddChartBubble(trendLineSignalShort and !trendLineSignalShort[1], trendLine2A, "Long", Color.Light_Green);
```

## Questions

### How do I build a Dynamic RSI indicator in ThinkOrSwim step-by-step?

Start by setting up global variables and input parameters using declare lower and input statements. Then build the core RSI calculations with NetChgAvg and TotChgAvg variables. Add the dynamic yellow average line using Average(DYNRSI, Volatility_Band), plot Bollinger Bands around this average, and finally incorporate automatic trend lines using Mobius's inertia-based algorithm. Each step builds on the previous one to create the complete system.

### What makes the Dynamic RSI different from a standard RSI indicator?

The Dynamic RSI combines five key components: traditional RSI with a smoothing yellow average line, adaptive Bollinger Bands that adjust to volatility, automatic trend line detection for objective support/resistance, squeeze detection for volatility compression, and multi-condition signal filtering. This creates a comprehensive system that adapts to market conditions rather than using fixed parameters.

### How do I set up the automatic trend lines in the Dynamic RSI code?

Use Mobius's inertia-based algorithm with three different lengths (150, 80, 40 periods). Calculate inertia values using InertiaAll(RSI, TrendLineLength), then create bull and bear trend lines using the formula Inertia - (HighestAll(AbsValue(Inertia - RSI)) * 0.8). Plot these as trend lines and add chart bubbles to detect when RSI touches the lines for objective trading signals.

### What parameters should I use for day trading vs swing trading with Dynamic RSI?

For day trading, use RSI period 8-10, Bollinger Band period 10-12, and trend line lengths 100/60/30 for more sensitive signals. For swing trading, use RSI period 21, Bollinger Band period 20, and trend line lengths 200/120/60 for more stable signals. Keith recommends 4-minute charts for optimal balance between signal frequency and accuracy.

### How does the squeeze detection work in the Dynamic RSI system?

Squeeze detection monitors the distance between upper and lower Bollinger Bands on the RSI. When this distance falls below 10 points (customizable), red dots appear indicating compression. This warns of low volatility periods that often precede explosive price movements. The squeeze works on the RSI level rather than price, providing earlier signals than traditional price-based squeeze indicators.

### What are the breakthrough signal conditions for the Dynamic RSI arrows?

Bullish signals require: RSI breaks below lower Bollinger Band AND lower band is below 45 AND RSI is below 30. Bearish signals require: RSI breaks above upper Bollinger Band AND upper band is above 55 AND RSI is above 70. These multiple conditions filter out false signals and ensure arrows only appear during extreme, high-probability setups.

### How do I create both upper and lower studies for the Dynamic RSI?

Create two separate indicators: Lower study shows complete RSI analysis with all lines, bands, and trend lines visible. Upper study uses declare upper and plots only arrows and squeeze dots on the price chart using the same calculation logic but simplified plotting. This keeps price charts clean while providing full analysis in the lower study window.

### Can I customize the Dynamic RSI colors and sensitivity settings?

Yes, extensively. Adjust RSI_Period (8-25), Volatility_Band (10-40), STDEV_Multiplier (1.5-3.0), and trend line lengths for different sensitivities. Change colors using SetDefaultColor() for all plot elements. Modify the squeeze threshold (default 10) and breakthrough signal thresholds (45/55 levels) to fine-tune for your trading style and market conditions.

## Related

- [How to Identify and Trade False Breakouts](https://tosindicators.com/research/false-breakouts)
- [Multi-Time Frame DMI](https://tosindicators.com/indicators/mtf-dmi)
- [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)
- [Commodities Tracker](https://tosindicators.com/indicators/commodities-tracker)
- [Trend Squeeze](https://tosindicators.com/indicators/trend-squeeze)

