Volume Bursts using Rvol & ATR & BB Squeeze

hboogie

Member
Plus
Hi Everyone!

First time posting and relatively new ToS user (about a year) and have learned a lot from the great group of contributors/mods on here!

I'm attempting to create a study where I can visually pickup volume spikes/bursts/surges primarily applicable to option premium contract charts, but adjustable enough so that it can apply to anything that has volume.

I've read this thread and while I'm not looking for a scan (yet) or a column (yet) I tried to pickup on some of the logic and apply it to something I've been working on.

https://usethinkscript.com/threads/thinkorswim-volume-spike-alert-and-scanner.1057/page-4

Apologies in advance but I have no formal thinkscript or programming experience or background. So logically I hopped on ChatGPT and spent the better half of two nights trying to come up with something close to what I'm trying to accomplish and hit a wall. This is the code I currently have running.

Code:
# Define input parameters
input maPeriod = 21;  # Smoothing EMA period
input rvolThreshold1 = 50;
input rvolThreshold2 = 100;
input rvolThreshold3 = 200;
input rvolThreshold4 = 500;
input rvolThreshold5 = 800;
input bbLength = 20;  # Bollinger Bands length
input atrLength = 9;  # ATR length for Bollinger Bands and BBSQ points
input bbsqMultiplier1 = 1.5;  # Multiplier for BBSQ condition 1
input bbsqMultiplier2 = 1.6;  # Multiplier for BBSQ condition 2
input bbsqMultiplier3 = 1.9;  # Multiplier for BBSQ condition 3

# User-defined Moving Average type
input maType = {default SMA, EMA};
input pctvolchg = 5;  # Percentage volume change
input vol_length = 20;  # Length for average volume calculation

# Calculate the relative volume change
def cumulativeVolume = Sum(volume, 10);
def avgVolume = (cumulativeVolume - cumulativeVolume[10]) / 10;
def rvolChange = ((volume - avgVolume) / avgVolume) * 100;

# Calculate the oscillator value between -1500 and 1500
def oscillatorValue = Min(Max(rvolChange, -1500), 1500);

# Choose the Moving Average type
def maValue;
switch (maType) {
    case SMA:
        maValue = Average(oscillatorValue, maPeriod);
    case EMA:
        maValue = ExpAverage(oscillatorValue, maPeriod);
}

# Calculate Bollinger Bands using user-defined ATR length
def atr = MovingAverage(AverageType.WILDERS, TrueRange(high, close, low), atrLength);
def bbDeviation = 2;
def bbUpper = maValue + bbDeviation * atr;
def bbLower = maValue - bbDeviation * atr;

# Identify BBSQ points based on user-defined multipliers
def bbsqCondition1 = close > bbsqMultiplier1 * atr;
def bbsqCondition2 = close > bbsqMultiplier2 * atr;
def bbsqCondition3 = close > bbsqMultiplier3 * atr;

# Calculate the total of the sum of 10 previous bars
def totalVolume = TotalSum(volume);

# Determine spike thresholds based on a 100% increase
def spikeThreshold = rvolChange > 100;

# Plot the zero line
plot ZeroLine = 0;
ZeroLine.SetPaintingStrategy(PaintingStrategy.LINE);
ZeroLine.SetDefaultColor(Color.GRAY);

# Plot BBSQ points as separate plots with colors in the lower pane
plot BBSQPointsWhite = if bbsqCondition1 then 0 else double.nan;
BBSQPointsWhite.SetPaintingStrategy(PaintingStrategy.POINTS);
BBSQPointsWhite.AssignValueColor(Color.WHITE);

plot BBSQPointsOrange = if bbsqCondition2 then 0 else double.nan;
BBSQPointsOrange.SetPaintingStrategy(PaintingStrategy.POINTS);
BBSQPointsOrange.AssignValueColor(Color.ORANGE);

plot BBSQPointsViolet = if bbsqCondition3 then 0 else double.nan;
BBSQPointsViolet.SetPaintingStrategy(PaintingStrategy.POINTS);
BBSQPointsViolet.AssignValueColor(Color.VIOLET);

# Plot colored points for the spike thresholds
plot SpikePoints = if spikeThreshold then 0 else double.nan;
SpikePoints.SetPaintingStrategy(PaintingStrategy.POINTS);
SpikePoints.AssignValueColor(Color.GREEN);

# Plot the oscillator in the lower pane
plot RelativeVolumeOscillator = maValue;
RelativeVolumeOscillator.AssignValueColor(
    if rvolChange >= rvolThreshold5 then Color.RED
    else if rvolChange >= rvolThreshold4 then Color.MAGENTA
    else if rvolChange >= rvolThreshold3 then Color.CYAN
    else if rvolChange >= rvolThreshold2 then Color.YELLOW
    else if rvolChange >= rvolThreshold1 then Color.GREEN
    else Color.GRAY
);
RelativeVolumeOscillator.SetLineWeight(1);
RelativeVolumeOscillator.HideTitle();

# Set the plot index to the lower pane
BBSQPointsWhite.SetPaintingStrategy(PaintingStrategy.POINTS);
BBSQPointsOrange.SetPaintingStrategy(PaintingStrategy.POINTS);
BBSQPointsViolet.SetPaintingStrategy(PaintingStrategy.POINTS);
SpikePoints.SetPaintingStrategy(PaintingStrategy.POINTS);

The logic and strategy is two-fold.

  1. The first is calculating RVOL based on percentages differences of total cumulative volume of "x" bars back (would like to make this user-defined)
  2. I then wanted to make sure this was in an oscillator format and within that oscillator line, paint different colors when different rvol thresholds are met. Again, these thresholds should be user-defined so that they can be applied to different securites that exhibit different volume characteristics. Currently, this is what the oscillator looks like
    Screenshot 2023-11-09 at 9.14.37 AM.png

  1. I then attemped to include Bollinger band squeeze logic so that i can reduce the amount areas where volume surges actually happen(false-postive reductions) I had trouble adding keltners so what I tried to do is base the squeeze and bands off ATR(9) (user-definable)

The thought here is then trigger different color points (white,orange,violet) based on how big of a difference the volume bars show when compared to atr(9). As I type this, I'm thinking this should be based off the sessions volume, which thinking about it might be ATR(1) if we're talking a 0Dte contract that doesn't have prior volume.

Ideally, the BBsq will pick when price is consolidating a bit, then the FIRST volume bar on a 1m chart prints the following conditions: higher than 100% of previous 10 bars based on atr(9) avg volume (this would paint a white point) An ORANGE point would be 200% greater than previous 11 bars (including the previous white point) and combined ATR() And Violet would be 500+% greater than 12 bars combined along with ATR() These thresholds should be user-defined. The logic here is to try and catch a surge and identify if the vol surge is sustained and not just a single bar. This all should be picked up from the 1M chart (defaul aggregation)

I put in a matype as a way to smooth the oscillator line, but don't think there is logic to actually get that done. I do have an EMA code which I want to associate itself with the price of the ticker- I don't think it does that. In other words, a 8 or 21ema (21 is defined in the code but it doesn't move much from the zero line) would move up and down with price within the oscillitor and give me insight on possible divergences.

There is a code i entered from the thread above about picking up on a % spike that seems to capture a spike, but needs adjusting.

I am open to completely re-designing the logic here and am not married to anything other than the visual aspect of the study. I want it to be an oscillator with a zero-line and the first conditions of rvol to match with the 4 colors presented. I do think the rvol thresholds and logic need adjusting since my goal is to capture when volume surges (both present in calls and puts and underlying stocks)

I also want to use the colored points visual to identify when the surge starts happening and the colors intensify(change) as volume bars surge higher. Especially with option contracts, lots of volume comes pouring in sometimes and can have a sudden affect in premium. Being able to see the volume spike earlier would be helpful.

The thought of using the squeeze was to try and limit the amount of false positives, but i think my implementation is off. I love the idea of using ATR as the measure by which rvol/squeeze's are calculated, but could be a bit off on implementation.

These are some images that can help visualize what I'm trying to get done. And again, open to completely re-working the logic. And maybe i'm over-complicating things, hoping to clear up some thoughts and get something workable.

Thanks!

H

**Shared Chart Link: https://tos.mx/LFDfRvW

rvolbars.png
rvol-sample.png
rvol-sample2.png
rvol_sample3.png
53320634309_96702f3343_5k.jpg
 
Last edited:
Just a further note on what the indicator would display on futures this morning. There is an intial volume “pulse” at the higher low. Volume rests, then explodes higher. In the application I’m trying to Build, you’ll have the oscillator change color in sequence, then print white dots, no dots, orange dot, the violet dots so long as volume keeps coming in and is sustained. Hope this helps

H.

1699625738984.png
 
To be clear that script paints bubbles when volume thresholds meet a multiplier above an SMA. I like the idea of the multiplier, which I tried to include. The multipliers would be adjustable and account for the white,orange,violet points being painted if the threshold requirements are met.

Basing the threshold multiplier off an EMA over a Getafe volume could work too. But I’d like to keep this as shown with an oscillator and contains two distinct elements. 4 color rvol thresholds that get painted on the oscillator line. And 3 colored points that are identified when big volume spikes on the 1m pop up.

The rvol logic can be associated with cumulative volume from “x” bars back. And the 1m volume spike/surge/burst logic can be associated with combining the “x” bars back condition along with a multiplier above the ATr() of avg volume or EMA (8,21,50 user-def) above volume.

Hope this helps.
 
Ok lets make the change with the logic here while incorporating what @samer800 did with converting the volume spike TV script. Found here : https://usethinkscript.com/threads/volume-spikes-tfo-for-thinkorswim.17144/

*The volume average should all be using the 1m aggregation.

RVOL Line Plot Painting:

So the first part would be the RVOL Oscillator painting 4 colors based on % increases over "x" bars back cumulatively.

CYAN,RED,YELLOW,GREEN

The multiplier is the amount % above the cumulative volume of "X" bars back

So the 4 inputs are user-defined multipliers and bars back

CYAN 50% & 3 bars back
RED 100% & 5 bars back
YELLOW 200% & 8 bars back
GREEN 300% & 12 bars back

Its the clustering of colors that would provide a strong signal that sustained volume is coming in.

Price Exhaustion using Keltner channels:

Usually price/premiums would start running once they've reached an exhaustive point. Sometimes it out of consolidation, but it's a bit more telling to be visually flagged when price is "extended"

So using a 40 period and 2.5 std dev setting on KC's within premium chart you get the following:
2023-11-14 14_04_49-Greenshot image editor.png


So the same script would then then paint candles that have touched, breached or are below the 2.5. Color would be MAGENTA. Above would be LIGHT_GREEN

Volume Spikes:

So this logic would paint the bubbles not on the price (upper) but along the zero line of the study

Code:
input VolumeMultiplier = 1.5;         # "Volume Multiplier"
input VolumeSmaLength = 100;          # "Volume SMA Length"
input only_valid_hl = yes;            # "Only Use Valid Highs & Lows"
input only_hammers_shooters = yes;    # "Only Use Hammers & Shooters")
input UseSameCloseVolumeSpikes = no;  # "Only Use Same-Close Volume Spikes"
input startTime = 0000;               # "Session Time"
input EndTime = 2359;                 # "0000-0000", "Session Time"

def na = Double.NaN;
def t = SecondsFromTime(startTime) >=0 and SecondsTillTime(EndTime) >=0;


#// Candle calculations

def valid_high1 = if only_valid_hl then high[1] > high[2] and high[1] > high[0] else yes;
def valid_low1 = if only_valid_hl then low[1] < low[2] and low[1] < low[0] else yes;

The changes would include multiple multipliers and they would trigger when each is a % above the Volume EMA length that's user-defined. In this case the 13ema.

So three multipliers would print the following bubble colors

1.5 > volume ema length (13ema) - WHITE

2.5 > volume ema length (13ema) - ORANGE

5 > volume ema length (13ema) - VIOLET

Multiplier and ema length would be customizable.

Hope this helps makes things a bit clearer.
 
Last edited:
This would be an example of knowing context when it comes to price. If below KC and lots of candles are magenta and rvol metrics are sustained and bubbles printed, that means calls could be getting unloaded.


2023-11-14 14_27_10-Screenshots.png



Perfect example as I was writing this post, the same contract reversed and went on to print close to 300%

Help! lol

2023-11-14 14_57_23-.SPXW231114C4500 - Charts - 42_____65 Main@thinkorswim [build 1979].png
 
Last edited:
Ok here is an update on the revamped code.

Screenshot 2023-11-20 at 11.22.17 PM.png



Code:
# ————————————————————————————
# Relative Volume Trend
# hboogie
# ————————————————————————————

# Define Rvol Thresholds as Multipliers
declare lower;

input rvolMultiplier1 = 1.5;
input rvolMultiplier2 = 2.0;
input rvolMultiplier3 = 3.0;
input rvolMultiplier4 = 5.0;
input rvolMultiplier5 = 8.0;

input maPeriod = 21;
input pctvolchg = 5;  # Percentage volume change
input pctvolchg1 = 10;

# Calculate the total of the sum of x previous bars
def cumulativeVolume = TotalSum(volume);

# Calculate the relative volume change
def avgVolume = (cumulativeVolume - cumulativeVolume[5]) / 5;
def rvolChange = ((volume - avgVolume) / avgVolume) * 100;

# Calculate the oscillator value between -1000 and 1000
def oscillatorValue = Min(Max(rvolChange, -1000), 1000);

# Choose the Moving Average type (Exponential)
def maValue = ExpAverage(oscillatorValue, maPeriod);

# Determine spike thresholds based on a % increase
def spikeThreshold = rvolChange > pctvolchg;
def spikeThreshold1 = rvolChange > pctvolchg1;

# Define Rvol Thresholds
def rvolThreshold1 = rvolMultiplier1 * avgVolume;
def rvolThreshold2 = rvolMultiplier2 * avgVolume;
def rvolThreshold3 = rvolMultiplier3 * avgVolume;
def rvolThreshold4 = rvolMultiplier4 * avgVolume;
def rvolThreshold5 = rvolMultiplier5 * avgVolume;

# Plot the zero line
plot ZeroLine = 0;
ZeroLine.SetPaintingStrategy(PaintingStrategy.LINE);
ZeroLine.SetDefaultColor(Color.GRAY);

# Plot colored points for the spike thresholds
plot SpikePoints = if spikeThreshold then 0 else double.nan;
SpikePoints.SetPaintingStrategy(PaintingStrategy.POINTS);
SpikePoints.AssignValueColor(Color.GREEN);

# Plot points
plot SpikePoints1 = if spikeThreshold1 then 0 else double.nan;
SpikePoints1.SetPaintingStrategy(PaintingStrategy.POINTS);
SpikePoints1.AssignValueColor(Color.VIOLET);

# Plot the oscillator in the lower pane (Exponential MA)
plot RelativeVolumeOscillator = maValue;
RelativeVolumeOscillator.AssignValueColor(
    if rvolChange >= rvolThreshold5 then Color.RED
    else if rvolChange >= rvolThreshold4 then Color.MAGENTA
    else if rvolChange >= rvolThreshold3 then Color.CYAN
    else if rvolChange >= rvolThreshold2 then Color.YELLOW
    else if rvolChange >= rvolThreshold1 then Color.GREEN
    else Color.GRAY
);
RelativeVolumeOscillator.SetLineWeight(1);
RelativeVolumeOscillator.HideTitle();
# Elder Impulse Indicator for ThinkorSwim
# @Ace_Trader 1/10/2019

input length = 13;

def EMA = MovAvgExponential(length=length)."AvgExp";


def impulseSignal = if (EMA < EMA[1])
then 0
else if (EMA > EMA[1])
then 1
else -1;

assignpriceColor(if impulseSignal == 1 then Color.White else if impulseSignal == 0 then Color.MAGENTA else Color.Yellow);


#END

So I need some guidance on re-defining cumulative volume using x previous bars. I was hoping to define that using ATR average volume, but don't know how to express that?

I think if i could redine that logic it would prevent the oscillator readings from the rvol threshold from triggering in low volume consolidation.

Which brings me to being able to tie the elder impulse logic below so that only spike points get triggered when EMA() is < EMA(1). Basically, spike points should only trigger when magenta bars are printed. This way a reversal from lower prices could be alerted when rvol picks up.

Any takers ?

Thanks,
H
 
Last edited:

Join useThinkScript to post your question to a community of 21,000+ developers and traders.

Similar threads

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
297 Online
Create Post

Similar threads

Similar threads

The Market Trading Game Changer

Join 2,500+ subscribers inside the useThinkScript VIP Membership Club
  • Exclusive indicators
  • Proven strategies & setups
  • Private Discord community
  • ‘Buy The Dip’ signal alerts
  • Exclusive members-only content
  • Add-ons and resources
  • 1 full year of unlimited support

Frequently Asked Questions

What is useThinkScript?

useThinkScript is the #1 community of stock market investors using indicators and other tools to power their trading strategies. Traders of all skill levels use our forums to learn about scripting and indicators, help each other, and discover new ways to gain an edge in the markets.

How do I get started?

We get it. Our forum can be intimidating, if not overwhelming. With thousands of topics, tens of thousands of posts, our community has created an incredibly deep knowledge base for stock traders. No one can ever exhaust every resource provided on our site.

If you are new, or just looking for guidance, here are some helpful links to get you started.

What are the benefits of VIP Membership?
VIP members get exclusive access to these proven and tested premium indicators: Buy the Dip, Advanced Market Moves 2.0, Take Profit, and Volatility Trading Range. In addition, VIP members get access to over 50 VIP-only custom indicators, add-ons, and strategies, private VIP-only forums, private Discord channel to discuss trades and strategies in real-time, customer support, trade alerts, and much more. Learn all about VIP membership here.
How can I access the premium indicators?
To access the premium indicators, which are plug and play ready, sign up for VIP membership here.
Back
Top