Volume Reversals Indicator for ThinkorSwim

BenTen

Administrative
Staff member
Staff
VIP
Lifetime
This indicator is just a simple modification of the original script called SJ_VSA_Reversals that was based on Volume Price Analysis (VPA). Someone wanted to turn the bubble signals into arrows so here it is.

With the arrows, it just makes it easier for the signals to be displayed on your chart. Moreover, you can use it to scan for new signals on the hourly, daily, etc. The Volume Reversals indicator also come with alerts (by default from the original).

5m9Daax.png

BDaUpsp.png


thinkScript Code

Code:
# Volume Reversals Indicator
# Original SJ_VSA_Reversals (found in thinkScript Lounge)
# Modified by BenTen at useThinkScript.com
# Replaced chart bubbles with arrows

# Arguments

def length = 40;

def displace = 0;

def volumeEMALength = 30;

def narrowSpreadFactor = 0.7;

def wideSpreadFactor = 1.5;

def aboveAvgVolfactor = 1.5;

def ultraHighVolfactor = 2;

def highCloseFactor = 0.70;

def lowCloseFactor = 0.25;

def GdojiFactor = 0.2; # C_RP

def WickFactor = 0.1; # C_RP

def shortTermLength = 5; #C_RP

def midTermLength = 15; #C_RP

def longTermLength = 40; #C_RP

input colorBars = {false, default true}; #C_RP

input trendText = {false, default true};

input volumeDefinitions = { false, default true };

input alerts = { default false, true };

# Calculations

rec spread = high - low;

def median = (high + low ) / 2;

rec avgVolume = compoundValue(volumeEMALength, ExpAverage(volume, volumeEMALength), Double.NaN);

# Calculate Volume moving average and it's standard deviation

rec sAvgVolume =  compoundValue(volumeEMALength, Average(volume, volumeEMALength), Double.NaN);

def sAvgVolumeSTD = stdev(sAvgVolume, volumeEMALength);

# check if the vloume has been decreasing in the past two days.

def isTwoDaysLowVol = (volume < volume[1] && volume[0] < volume[2]);

# Calculate Range information

def avgSpread = WildersAverage(spread, volumeEMALength)[0];

rec isWideSpreadBar = (spread > (wideSpreadFactor * avgSpread));

rec isNarrowSpreadBar = (spread < (narrowSpreadFactor * avgSpread));

# Price information

rec isUpBar = close > close[1];

rec isDownBar = close < close[1];

# Check if the close is in the Highs/Lows/Middle of the bar.

# C_RP 20100809

# original code - def x1 = if (close == low) then avgSpread else (spread / (close - low));

def x1 = if (high==low) then 2.0 else if (close == low) then 2.65 else (spread / (close - low));

# C_RP try the line below with various divisors totalSum result in a minimum of 2.3 on a spread of 1 pip instead of using a fixed 2.3 as in the line above

# def x1 = if (high==low) then 2.0 else if (close == low) then (spread / 0.43 ) else (spread / (close - low));

def isUpCloseBar = (x1 < 2);

def isDownCloseBar = (x1 > 2);

def isMidCloseBar = (x1 < 2.2 && x1 > 1.8);

def isVeryHighCloseBar = (x1 < 1.35);

# C_RP 20100809 added isVeryLowCloseBar

def isVeryLowCloseBar = (x1 >= 2.65);


# Trend Definitions


rec fiveDaysSma = compoundValue(5, Average(close, 5)[0], Double.NaN);

def LongTermTrendSlope = LinearRegressionSlope(price = fiveDaysSma, length = longTermLength)[0]; # 40

def MiddleTermTrendSlope = LinearRegressionSlope(price = fiveDaysSma, length = midTermLength)[0]; # 15

def ShortTermTrendSlope = LinearRegressionSlope(price = fiveDaysSma, length = shortTermLength)[0]; # 5


#  VSA Definitions



# upTHRUST - CRITERIA   

# utbar

rec isUpThrustBar = isWideSpreadBar && isDownCloseBar &&  ShortTermTrendSlope > 0 && middleTermTrendSlope > 0; #C_RP added positive middleTermTrendSlope requirement to filter out upThrusts in trends that are only short term. Consider adding longTermTrendSlope requirement as well.

# utcond1

def upThrustConditionOne = (isUpThrustBar[1] && isDownBar);

# utcond2

def upThrustConditionTwo = (isUpThrustBar[1] && isDownBar[0] && volume > volume[1]);

# utcond3

def upThrustConditionThree = (isUpThrustBar[0] && volume > 2 * sAvgVolume[0]);

# scond1

rec isConfirmedUpThrustBar = (upThrustConditionOne OR upThrustConditionTwo OR upThrustConditionThree);

# scond

rec isNewConfirmedUpThrustBar = (isConfirmedUpThrustBar[0] && !isConfirmedUpThrustBar[1]);

# Two Period UpThrust Bar

rec isTwoPerUpT = isUpBar[1] && isWideSpreadBar[1] &&  isDownBar[0] && isDownCloseBar[0] && !isUpThrustBar[0] && (absValue(open[1] - close[0]) < (GdojiFactor * spread[1])) ;

# Three Period UpThrust Bar

rec isThreePerUpT = isUpBar[2] && isWideSpreadBar[2] &&  isDownBar[0] && isDownCloseBar[0] && !isUpThrustBar[0] && (absValue(open[2] - close[0]) < (GdojiFactor * spread[2])) ;


# GRAVESTONE DOJI - CRITERIA

#  C_RP 20100816

# rec isGraveDojiBar = (spread > avgSpread) && (open == low) && (close == low); totally strict Gravestone Doji. Revised version below identifies a candle with above average spread, a real body smaller than 20% of the spread, and a lower wick less than 10% of the spread as a Gravestone Doji pictured as a white triangle above the candle.

rec isGraveDojiBar = (spread > avgSpread) && (absValue(open - close) < (GdojiFactor * spread)) &&  ((absValue(close - low) < (WickFactor * spread)) or (absValue(open - low) < (WickFactor * spread))); # less strict Gravestone Doji


# LIKELY REVERSAL - CRITERIA

#  trbar

def reversalLikelyBar = (volume[1] > sAvgVolume[0] && isUpBar[1] && isWideSpreadBar[1] && isDownBar[0] && isDownCloseBar && isWideSpreadBar[0] && LongTermTrendSlope > 0 && high == Highest(high, 10)[0]);


# PSEUSO-upTHRUST - CRITERIA           

# hutbar

rec isPseudoUpThrustBar = (isUpBar[1] && (volume[1] > aboveAvgVolfactor * sAvgVolume[0]) && isDownBar[0] && isDownCloseBar && !isWideSpreadBar[0] && !isUpThrustBar[0]);

# hutcond

def pseudoUpThrustConfirmation = (isPseudoUpThrustBar[1] && isDownBar[0] && isDownCloseBar && !isUpThrustBar[0]);


# FAILED-upTHRUST - CRITERIA

# C_RP Failed UpThrustConfirmation or pseudoUpThrustConfirmation occurs when the close of bar following such confirmation is not lower than the close of the confirmation bar

rec isFailedUpThrustConfirmation = (isNewConfirmedUpThrustBar[1] or pseudoUpThrustConfirmation[1]) && close[0] >= close[1];


# WEAKNESS BAR - CRITERIA

# tcbar

def weaknessBar = (isUpBar[1] && high[0] == Highest(high, 5)[0] && isDownBar[0] && (isDownCloseBar OR isMidCloseBar) && volume[0] > sAvgVolume[0] && !isWideSpreadBar[0] && !isPseudoUpThrustBar[0]);


# DOWNTREND STRENGTH (MARKET WEAKNESS) - CRITERIA

# stdn, stdn0, stdn1, stdn2

def strengthInDownTrend =  (volume[0] > volume[1] && isDownBar[1] && isUpBar[0] && (isUpCloseBar OR isMidCloseBar) && ShortTermTrendSlope < 0 && MiddleTermTrendSlope < 0);

def strengthInDownTrend0 = (volume[0] > volume[1] && isDownBar[1] && isUpBar[0] && (isUpCloseBar OR isMidCloseBar) && ShortTermTrendSlope < 0 && MiddleTermTrendSlope < 0 && LongTermTrendSlope < 0);

def strengthInDownTrend1 = (volume[0] > (sAvgVolume[0] * aboveAvgVolfactor) && isDownBar[1] && isUpBar[0] && (isUpCloseBar OR isMidCloseBar) && ShortTermTrendSlope < 0 && MiddleTermTrendSlope < 0 && LongTermTrendSlope < 0);

def strengthInDownTrend2 = (volume[1] < sAvgVolume[0] && isUpBar[0] && isVeryHighCloseBar && volume[0] > sAvgVolume[0] && ShortTermTrendSlope < 0);


# CONFIRMATION DOWNTREND STRENGTH (MARKET WEAKNESS) - CRITERIA

rec bycond1 = (strengthInDownTrend OR strengthInDownTrend1);

# bycond

def isStrengthConfirmationBar = (isUpBar[0] && bycond1[1]);

# bycond2 C_RP UpClose on higher volume with all slopes down adds extra strength

def isStrengthConfirmationBar2 = (isUpBar[0] && isUpCloseBar[0] && volume[0] > volume[1] && longtermtrendslope < 0 && bycond1[1]);


# FAILED DOWNTREND STRENGTH (MARKET WEAKNESS) - CRITERIA

# Failed strength in downtrend signal force a follow-up bar that closes below mid-point of confirmaton bar C_RP

def isFailedStrengthSignal = (isStrengthConfirmationBar[1] or isStrengthConfirmationBar2[1] or strengthinDownTrend2[1])&& close[0] <= (close[1] - (spread[1]/2));


# STOPPING VOLUME AT BOTTOM (LIKELY REVERSAL) - CRITERIA

# stvol

def stopVolBar = low[0] == Lowest(low, 5)[0] && (isUpCloseBar OR isMidCloseBar) && volume[0] > aboveAvgVolfactor * sAvgVolume[0] && LongTermTrendSlope < 0;


# STOPPING VOLUME AT TOP (LIKELY REVERSAL) - CRITERIA

# C_RP stvol at highs - the opposite of stvol

def stopVolBarHighs = high[0] == Highest(high, 5)[0] && (isDownCloseBar OR isMidCloseBar) && volume[0] > aboveAvgVolfactor * sAvgVolume[0] && LongTermTrendSlope > 0;


# NO DEMAND - CRITERIA

# ndbar, nsbar

def noDemandBar = (isUpBar[0] && isNarrowSpreadBar[0] && isTwoDaysLowVol && isDownCloseBar);

# C_RP 20100809


# NO SUPPLY - CRITERIA

# def noSupplyBar = (isDownBar[0] && isNarrowSpreadBar[0] && isTwoDaysLowVol && isDownCloseBar);

def noSupplyBar = (isDownBar[0] && isNarrowSpreadBar[0] && isTwoDaysLowVol && isUpCloseBar);


# TEST FOR SUPPLY - CRITERIA

# lvtbar, lvtbar1, lvtbar2

rec supplyTestBar = (isTwoDaysLowVol && low[0] < low[1] && isUpCloseBar);


# TEST FOR SUPPLY IN AN UPTREND - CRITERIA

def supplyTestInUpTrendBar = (volume[0] < sAvgVolume[0] && Low[0] < Low[1] && isUpCloseBar && LongTermTrendSlope > 0 && MiddleTermTrendSlope > 0 && isWideSpreadBar[0]);


# SUCCESSFUL FIRST TEST FOR SUPPLY - CRITERIA

def successfulSupplyTestBar = (supplyTestBar[1] && isUpBar[0] && isUpCloseBar);


# SUCCESSFUL SECOND TEST FOR SUPPLY - CRITERIA

def successfulSupplyTestBar2 = (successfulsupplyTestBar[1] && isUpBar[0] && x1 <= 2 && volume[0] > volume[1]); # C_RP x1 finds Mid and UpCloseBars


# DISTRIBUTION - CRITERIA         

# dbar

def distributionBar = (volume[0] > ultraHighVolfactor * sAvgVolume[0] && isDownCloseBar && isUpBar[0] && ShortTermTrendSlope > 0 && MiddleTermTrendSlope > 0 && !isConfirmedUpThrustBar[0] && !isUpThrustBar[0]);


# EFFORT TO MOVE UP - CRITERIA

# eftup, eftupfl, eftdn

def effortToMoveUpBar = (high[0] > high[1] && low[0] > low[1] && Close[0] > Close[1] && Close[0] >= ((high[0] - low[0]) * highCloseFactor + low[0]) && spread[0] > avgSpread && volume[0] > volume[1]);


# FAILED EFFORT TO MOVE UP - CRITERIA

def failedEffortUpMove = (effortToMoveUpBar[1] && (isUpThrustBar[0] OR upThrustConditionOne OR upThrustConditionTwo OR upThrustConditionThree));


# EFFORT TO DOWN - CRITERIA

def effortToMoveDownBar = ( ((high[0] < high[1]) OR (isWideSpreadBar && volume[0] > 1.5 * sAvgVolume[0]))  && low[0] < low[1] && close[0] < close[1] && Close[0] <= ((high[0] - low[0]) * lowCloseFactor + low[0]) && spread[0] > avgSpread && volume[0] > volume[1]);

#C_RP old code - def effortToMoveDownBar = (high[0] < high[1] && low[0] < low[1] && close[0] < close[1] && Close[0] <= ((high[0] - low[0]) * lowCloseFactor + low[0]) && spread[0] > avgSpread && volume[0] > volume[1]);


#  PLOT DEFINITIONS


def uSMA = Average(volume[-displace], length);

def dSMA = Average(-volume[-displace], length);

def ZeroLine = 0;

def Condition1 = shortTermTrendSlope > 0 and MiddleTermTrendSlope > 0 and longtermtrendslope > 0;

def Condition2 = shortTermTrendSlope > 0 and MiddleTermTrendSlope > 0 and longtermtrendslope < 0;

def Condition3 = shortTermTrendSlope > 0 and MiddleTermTrendSlope < 0 and longtermtrendslope < 0;

def Condition4 = shortTermTrendSlope < 0 and MiddleTermTrendSlope < 0 and longtermtrendslope < 0;

def Condition5 = shortTermTrendSlope < 0 and MiddleTermTrendSlope > 0 and longtermtrendslope > 0;

def Condition6 = shortTermTrendSlope < 0 and MiddleTermTrendSlope < 0 and longtermtrendslope > 0;


# Candle definitions

def SafeReversal = double.nan;

# plot null = double.nan;


# SELECTED REVERSAL CONDITIONS


# LIKELY REVERSAL AT THE TOP #1

# (Top - Red DownArrow)

def ConditionDown1 = if isNewConfirmedUpThrustBar && (upThrustConditionTwo or upThrustConditionThree) then (high + 4 * tickSize()) else Double.NAN;


# (Top - Red Triangle)

def ConditionDown2 = if isNewConfirmedUpThrustBar && upThrustConditionOne then (high + 2 * tickSize()) else Double.NAN;


# (Middle - Magenta Circle)

def ConditionDown3 = if effortToMoveDownBar && !(shortTermTrendSlope < 0 && MiddleTermTrendSlope < 0 && longTermTrendSlope < 0) then (median) else

Double.NAN;

# AddChartBubble(ConditionDown2, Volume, "Reversal Alert", Color.Red, Yes);


# LIKELY REVERSAL AT THE TOP #2

# (Top - Red Circle)

def ConditionDown4 = if reversalLikelyBar then (high + 6 * tickSize()) else Double.NAN;


# (Top - Yellow circle)

def ConditionDown5 = if stopVolBarHighs then (high + 7 * tickSize()) else Double.NAN;

#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

# LIKELY REVERSAL AT THE BOTTOM #1

# (Bottom - Green Arrow Up) #1

def ConditionUp1 = if isStrengthConfirmationBar then (low - 4 * tickSize()) else Double.NAN;


# (Bottom - Green Arrow Up) #2

def ConditionUp2 = if isStrengthConfirmationBar2 then (low - 7 * tickSize()) else Double.NAN;


# (Middle - Green Circle)

def ConditionUp3 = if effortToMoveUpBar then (median) else Double.NAN;

# LIKELY REVERSAL AT THE BOTTOM #2

# (Bottom - Green Circle)

def ConditionUp4 = if stopVolBar then (low - 2 * tickSize()) else Double.NAN;


# (Bottom - Green Triangle)

def ConditionUp5 = if strengthInDownTrend2 then (low - 3 * tickSize()) else Double.NAN;

# Arrows below based on the following
#AddChartBubble(ConditionUp4 && ConditionUp5, Volume, concat("Buy @ $", Close), Color.Green, Yes);
#AddChartBubble(ConditionUp1 && ConditionUp2 && ConditionUp3, Volume, concat("Buy @ $", Close), Color.Green, Yes);
#AddChartBubble(ConditionDown1 && ConditionDown4 && ConditionDown5, Volume, concat("Sell @ $", Close), Color.Red, Yes);
#AddChartBubble(ConditionDown1 && ConditionDown2 && ConditionDown3,  Volume, concat("Sell @ $", Close), Color.Red, Yes);

# buy and sell signals

def Buy45 = ConditionUp4 && ConditionUp5;
plot bullish45 = Buy45;
bullish45.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
bullish45.SetDefaultColor(Color.CYAN);
bullish45.SetLineWeight(3);

def Buy123 = ConditionUp1 && ConditionUp2 && ConditionUp3;
plot bullish123 = Buy123;
bullish123.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
bullish123.SetDefaultColor(Color.CYAN);
bullish123.SetLineWeight(3);

def Bearish145i = ConditionDown1 && ConditionDown4 && ConditionDown5;
plot bearish145 = Bearish145i;
bearish145.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
bearish145.SetDefaultColor(Color.CYAN);
bearish145.SetLineWeight(3);

def Bearish123i = ConditionDown1 && ConditionDown2 && ConditionDown3;
plot bearish123 = Bearish123i;
bearish123.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
bearish123.SetDefaultColor(Color.CYAN);
bearish123.SetLineWeight(3);

# Alerts
Alert(ConditionUp4 && ConditionUp5, concat("Buy @ $", Close), Alert.BAR, Sound.Bell);
Alert(ConditionUp1 && ConditionUp2 && ConditionUp3, concat("Buy @ $", Close), Alert.BAR, Sound.Bell);
Alert(ConditionDown1 && ConditionDown4 && ConditionDown5, concat("Sell @ $", Close), Alert.BAR, Sound.Chimes);
Alert(ConditionDown1 && ConditionDown2 && ConditionDown3, concat("Sell @ $", Close), Alert.BAR, Sound.Chimes);

Shareable Link

https://tos.mx/1iSyt6

Video Tutorial

 

Attachments

  • 5m9Daax.png
    5m9Daax.png
    83.7 KB · Views: 244
  • BDaUpsp.png
    BDaUpsp.png
    84.5 KB · Views: 248
Thanks for sharing. Do we need to change the code if we want to scan in daily timeframe?
 
@Nick If you want to scan for signals just use the Scanner. No need to change anything in the code.
 
A really nice script! Thanks for sharing(y)

I like that it doesn`t repaint and from my initial testing, it seems to work really well on the 5min timeframe for those trading intraday.
 
Another question, can a label be added to the chart when ever a VRI signal is confirmed? Reason i ask is because the VRI arrow has a tendency to hide behind the "Hi" bubble (see pic) and this can be a little confusing sometimes. Would be much more clearer if there was a label on the chart showing once the signal is confirmed.

lEIEpYx.png
 

Attachments

  • lEIEpYx.png
    lEIEpYx.png
    273.9 KB · Views: 217
Last edited by a moderator:
@zeek Add the following to the bottom of your script

Code:
AddChartBubble(bullish45, low, "VRI", Color.GREEN, no);
AddChartBubble(bearish145, high, "VRI", Color.RED, yes);
AddChartBubble(bullish123, low, "VRI", Color.GREEN, no);
AddChartBubble(bearish123, high, "VRI", Color.RED, yes);
 
Thanks for this @BenTen but instead of a chart bubble, can a label be added as well/instead once the signal is confirmed and stays visible as long as the signal is there?

I tried adding the following code line at the end but it doesn`t seem to work so my guess is the code is wrong somehow.

Code:
addlabel(bearish123, high, "VRI", Color.RED, yes);
 
@zeek Don't know if this will work but try it:

Code:
AddLabel(yes, "VRI", if bearish123 or bearish145  then Color.RED else if bullish45 or bullish123 then Color.GREEN else Color.WHITE);
 
@mansor Just some random naming I did for the arrows. You would have to look at the original indicator to understand what the condition means.
 
Ben - Previously, I was trying vri that I use it as an indicator & it gave me "toocomplexexception". Now, I don't know what I'm doing wrong here..So I copy & pasted script from post#1 & thinkscript editor reads "exactly one plot expected".
 
@mansor See message #23 above.

If you're combining multiple plots into one scanner, sometimes it wouldn't work. So, the solution would be to create a scanner for each signal.
 
@mansor See message #23 above.

If you're combining multiple plots into one scanner, sometimes it wouldn't work. So, the solution would be to create a scanner for each signal.
okay - I'm not a coder, so how would you recommend me to figure out which section of this script in post#1 is for bullish condition/criteria? Thanks again but I havent be able to figure it out so far
 
@mansor Are you asking for the name of each plot so that you can load them up in your scanner? If so:
  • Bullish = Buy45 and Buy123
  • Bearish = Bearish145i and Bearish123i
There are four different plots in total.
 

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

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
290 Online
Create Post

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