Implied Volatility (IV) Rank & Percentile for ThinkorSwim

I've come this far getting my IV Percentile Script made for my watchlist. But I think something is missing. I can't tell if it's accurate for one, since I pulled this out of someone elses script. And then I also am getting a NaN result on about every 5 stocks out of 100 on my watchlist. Hoping someone knows what is wrong.

Thank you in advance!

Code:
input days_back = 252;

# implied volatility
# using proxies for futures

def df = if (GetSymbol() == "/ES") then close("VIX") / 100 else if (GetSymbol() == "/CL") then close("OIV") / 100  else if (GetSymbol() == "/GC") then close("GVX") / 100  else if (GetSymbol() == "/SI") then close("VXSLV") / 100  else if (GetSymbol() == "/NQ") then close("VXN") / 100  else if (GetSymbol() == "/TF") then close("RVX") / 100  else if (GetSymbol() == "/YM") then close("VXD") / 100  else if (GetSymbol() == "/6E") then close("EVZ") / 100  else if (GetSymbol() == "/ZN") then close("VXTYN") / 100  else imp_volatility();
def df1 = if !IsNaN(df) then df else df[-1];
# calculate the IV percentile
# how many times over the past year, has IV been below the current IV
def counts_below = fold i = 1 to days_back + 1 with count = 0  do    if df1[0] > df1[i] then count + 1   else count;
def iv_percentile = Round(counts_below / days_back * 100.0, 0);
plot IVPercentile = iv_percentile;


vh3ut3e.png



biSIlkv.png
 
I've come this far getting my IV Percentile Script made for my watchlist. But I think something is missing. I can't tell if it's accurate for one, since I pulled this out of someone elses script. And then I also am getting a NaN result on about every 5 stocks out of 100 on my watchlist. Hoping someone knows what is wrong.

Thank you in advance!

Code:
input days_back = 252;

# implied volatility
# using proxies for futures

def df = if (GetSymbol() == "/ES") then close("VIX") / 100 else if (GetSymbol() == "/CL") then close("OIV") / 100  else if (GetSymbol() == "/GC") then close("GVX") / 100  else if (GetSymbol() == "/SI") then close("VXSLV") / 100  else if (GetSymbol() == "/NQ") then close("VXN") / 100  else if (GetSymbol() == "/TF") then close("RVX") / 100  else if (GetSymbol() == "/YM") then close("VXD") / 100  else if (GetSymbol() == "/6E") then close("EVZ") / 100  else if (GetSymbol() == "/ZN") then close("VXTYN") / 100  else imp_volatility();
def df1 = if !IsNaN(df) then df else df[-1];
# calculate the IV percentile
# how many times over the past year, has IV been below the current IV
def counts_below = fold i = 1 to days_back + 1 with count = 0  do    if df1[0] > df1[i] then count + 1   else count;
def iv_percentile = Round(counts_below / days_back * 100.0, 0);
plot IVPercentile = iv_percentile;

why do you have -1 for an offset?
def df1 = if !IsNaN(df) then df else df[-1];

if there is an error, i think it should read the previous value, not the future value.
might need to add a check if bar#1.
def df1 = if barnumber() == 1 then 0 else if !IsNaN(df) then df else df[1];


when using fold, use getvalue, to read other values

def counts_below =
fold i = 1 to days_back + 1
with count = 0
do if df1[0] > getvalue(df1, i) then count + 1
else count;
 
Last edited:
Is there a way to create a custom quote column in a watchlist for IV Rank?
IV Rank watchlist script
HA03zy3.png

Ruby:
# ------------------------START BELOW THIS LINE--------------------------
#
# tastytrade/dough Research Team
# Michael Rechenthin, Ph.D.
# Follow me on twitter:  @mrechenthin
#
# IV Rank is a description of where the current IV lies in comparison
# to its yearly high and low IV
#
# IV Percentile gives the percentage of days over the last year, that
# were below the current IV.  If the IV Rank is above 50%, then
# the script will highlight it green; otherwise red.
#
# For information on the two, see Skinny on Options Data Science,
# titled "IV Rank and IV Percentile (w/ thinkscript)" on Nov 12, 2015
# http://ontt.tv/1Nt4fcS
#
# version 3.2
#
declare hide_on_intraday; # do not display when using intra-day plots
input days_back = 252; # it is most common to use 1-year (or 252 trading days)


def x;
if GetAggregationPeriod() > AggregationPeriod.DAY {
x=1;
} else {
x=2;
}

# implied volatility
# using proxies for futures

def df = if (GetSymbol() == "/ES") then close("VIX") / 100
else if (GetSymbol() == "/CL") then close("OIV") / 100
else if (GetSymbol() == "/GC") then close("GVX") / 100
else if (GetSymbol() == "/SI") then close("VXSLV") / 100
else if (GetSymbol() == "/NQ") then close("VXN") / 100
else if (GetSymbol() == "/TF") then close("RVX") / 100
else if (GetSymbol() == "/YM") then close("VXD") / 100
else if (GetSymbol() == "/6E") then close("EVZ") / 100
else if (GetSymbol() == "/6J") then close("JYVIX") / 100
else if (GetSymbol() == "/6B") then close("BPVIX") / 100
else if (GetSymbol() == "/ZN") then close("TYVIX") / 100
else if (Getsymbol() == "/ZW") then close("WIV") / 100
else if (Getsymbol() == "/ZB") then imp_volatility("TLT")
else if (Getsymbol() == "/ZC") then imp_volatility("CORN")
else if (Getsymbol() == "/ZS") then imp_volatility("SOYB")
else if (Getsymbol() == "/KC") then imp_volatility("JO")
else if (Getsymbol() == "/NG") then imp_volatility("UNG")
else if (Getsymbol() == "/6S") then imp_volatility("FXF")
else imp_volatility();


def df1 = if !IsNaN(df) then df else df[-1];


# calculate the IV rank
def low_over_timespan = Lowest(df1, days_back);
def high_over_timespan = Highest(df1, days_back);
def iv_rankCALC = Round( (df1 - low_over_timespan) / (high_over_timespan - low_over_timespan) * 100.0, 0);
plot iv_rank = if !isNan(iv_rankCALC) then iv_rankCALC else 0;
AddLabel(yes, iv_rank, if iv_rank < 50 then Color.RED else
if iv_rank > 50 then Color.GREEN else  color.light_gray);

#AssignBackgroundColor(
#if iv_rank < 50 then Color.RED else
#if iv_rank > 50 then Color.GREEN else  color.light_gray);
 
Newbie here. Thanks for the script.

I'm not sure if this thread is the latest IVR info, but maybe this will help someone using futures.

I'm working with /GC and noticed that the implied volatility (as plotted by the ImpVolatility study) was identical to the IV Rank value and plot in this script.

Issue 1:
  • GetSymbol("/GC") returns "/GC:XCEC"
    • Note: entering /GC in the chart defaults to current month = June 2023, but GetSymbol() still returns "/GC:XCEC"
  • GetSymbol("/GCM23") returns "/GCM23:/XCEC"
    • Note: entering "/GCM23" in the chart explicitly specifies June 2023
Therefore, if I am trying to use "/GC" or "/GCM23", the code block falls through and ends up using imp_volatility()

Issue 2:

I modified the code to search for "/GC" and "/GCM23" which successfully matches and uses "GVX".

However, it looks like GVX is no longer the Gold Volatility Index - instead the Gold Volatility Index is "GVZ" per (CBOE). I'm not sure about the other futures.

I'm new to thinkscript (not to coding), I couldn't find a way to parse GetSymbol(), therefore the script will need to be updated each time the current futures month changes (please let me know if I'm wrong or there is a workaround)

Here are the changes for June Gold 2023. Note that something similar would need to be done for the other futures used (in the code or not in the code), or the script will use the out of the box ImpVolatility study.

Original Snippet:

Code:
def df = if (GetSymbol() == "/ES") then close("VIX") / 100
else if (GetSymbol() == "/GC") then close("GVX") / 100
...
else imp_volatility();

Modified Snippet:
Code:
def df = if (GetSymbol() == "/ES") then close("VIX") / 100
else if (GetSymbol() == "/GC:XCEC") then close("GVZ") / 100
else if (GetSymbol() == "/GCM23:XCEC") then close("GVZ") / 100
...
else imp_volatility();

Hope this helps someone.
 
Just a note: I used the code as in #1 and it looks fine in the Watchlist.

The output is the same as the "Tasty IV Percentile". When I get NaN (happens rarely) the Tasty plot fails too.
So it seems it works perfectly fine.

Additionally I took the default TOS IV_Percentlile script:

Code:
def vol = impVolatility();
rec data = if !isNaN(vol) then vol else data[1];
def hi = highest(data,252);
def lo = lowest(data,252);
plot perct = (data - lo)*100 / (hi - lo);

..and put it into Custom 1 and renamed it IV_Rank2 to avoid confusion.

PS: I love the Tasty CheatSheet - but not so much the quality. Here the same in a cleaner PDF: (I have it also in ODG)
 

Attachments

  • tasty.pdf
    42.6 KB · Views: 485
Last edited:
in the Scan->Stock Hacker
i added a custom filter of TastyTradeIVR > 50
but i am still getting stocks with low IVR
what i should put in the filter to get IVR>50 ?

any ideas ?

thanks
 
Last edited by a moderator:
in the Scan->Stock Hacker
i added a custom filter of TastyTradeIVR > 50
but i am still getting stocks with low IVR
what i should put in the filter to get IVR>50 ?

any ideas ?

thanks
Moved your question to this thread which should further your coding journey.
 
IV Rank watchlist script
View attachment 16213
Ruby:
# ------------------------START BELOW THIS LINE--------------------------
#
# tastytrade/dough Research Team
# Michael Rechenthin, Ph.D.
# Follow me on twitter:  @mrechenthin
#
# IV Rank is a description of where the current IV lies in comparison
# to its yearly high and low IV
#
# IV Percentile gives the percentage of days over the last year, that
# were below the current IV.  If the IV Rank is above 50%, then
# the script will highlight it green; otherwise red.
#
# For information on the two, see Skinny on Options Data Science,
# titled "IV Rank and IV Percentile (w/ thinkscript)" on Nov 12, 2015
# http://ontt.tv/1Nt4fcS
#
# version 3.2
#
declare hide_on_intraday; # do not display when using intra-day plots
input days_back = 252; # it is most common to use 1-year (or 252 trading days)


def x;
if GetAggregationPeriod() > AggregationPeriod.DAY {
x=1;
} else {
x=2;
}

# implied volatility
# using proxies for futures

def df = if (GetSymbol() == "/ES") then close("VIX") / 100
else if (GetSymbol() == "/CL") then close("OIV") / 100
else if (GetSymbol() == "/GC") then close("GVX") / 100
else if (GetSymbol() == "/SI") then close("VXSLV") / 100
else if (GetSymbol() == "/NQ") then close("VXN") / 100
else if (GetSymbol() == "/TF") then close("RVX") / 100
else if (GetSymbol() == "/YM") then close("VXD") / 100
else if (GetSymbol() == "/6E") then close("EVZ") / 100
else if (GetSymbol() == "/6J") then close("JYVIX") / 100
else if (GetSymbol() == "/6B") then close("BPVIX") / 100
else if (GetSymbol() == "/ZN") then close("TYVIX") / 100
else if (Getsymbol() == "/ZW") then close("WIV") / 100
else if (Getsymbol() == "/ZB") then imp_volatility("TLT")
else if (Getsymbol() == "/ZC") then imp_volatility("CORN")
else if (Getsymbol() == "/ZS") then imp_volatility("SOYB")
else if (Getsymbol() == "/KC") then imp_volatility("JO")
else if (Getsymbol() == "/NG") then imp_volatility("UNG")
else if (Getsymbol() == "/6S") then imp_volatility("FXF")
else imp_volatility();


def df1 = if !IsNaN(df) then df else df[-1];


# calculate the IV rank
def low_over_timespan = Lowest(df1, days_back);
def high_over_timespan = Highest(df1, days_back);
def iv_rankCALC = Round( (df1 - low_over_timespan) / (high_over_timespan - low_over_timespan) * 100.0, 0);
plot iv_rank = if !isNan(iv_rankCALC) then iv_rankCALC else 0;
AddLabel(yes, iv_rank, if iv_rank < 50 then Color.RED else
if iv_rank > 50 then Color.GREEN else  color.light_gray);

#AssignBackgroundColor(
#if iv_rank < 50 then Color.RED else
#if iv_rank > 50 then Color.GREEN else  color.light_gray);
Merry, Thanks for your awesome work. Do you happen to have or perhaps adjust this IVRank column so that I can see the real-time change in IV for SPX/SPY/QQQ options within the past 5 minutes? I'd like to add a column to my 0-3dte WL that can highlight %changes to IV real-time. Thanks!
 

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
245 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