# ------------------------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.7
# updated by WhitePath from www.OptionMarketMentor.com (6/25/2024)
# - Fixed IV Percentile always 0 and calculate correct Percentile
# - Added Average IV range with different COLOR
# - Added optional plot for IV Rank and Percentile and IV Cloud
# - Added optional level line for IV at low and average
# - Commented out hide on intraday (should work for any period)
# (does not work for new stocks with less than a year of data)
#
declare lower;
#declare hide_on_intraday; # do not display when using intra-day plots
input IV_Year_Trading_Days = 252; # it is most common to use 1-year (or 252 trading days)
input IV_HalfYear_Trading_Days = 126; # it is most common to use 2-quarter (or 120 trading days)
input IV_Quarter_Trading_Days = 63; # it is most common to use 1-quarter (or 60 trading days)
input IV_Month_Trading_Days = 21; # it is most common to use 1-month (or 20 trading days)
input IV_Threshold_Low = 30; # it is the low threshold on the chart
input IV_Threshold_High = 50; # it is the high threshold on the chart
input IV_Show_High_Area = no;
input IV_Show_All_Labels = yes;
# -----------------------------------
# Limit this to Daily Chart only (Disabled by WhitePath)
# -----------------------------------
#def intraDay;
#if GetAggregationPeriod() < AggregationPeriod.DAY {
#intraDay=1;
#} else {
#intraDay=2;
#}
#AddLabel(yes, "IV Rank for " + GetAggregationPeriod(), COLOR.GRAY);
#AddLabel(yes, if (intraDay==1) then "IV Rank Not available for " + GetAggregationPeriod() else "");
# -----------------------------------
# IV calculations
# -----------------------------------
DefineGlobalColor("IV-COLOR-LOW", COLOR.LIME);
DefineGlobalColor("IV-COLOR-AVG", COLOR.GRAY);
DefineGlobalColor("IV-COLOR-HIGH", COLOR.PINK);
# using proxies for futures
# -----------------------------------
def iv_data = 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();
# thanks to Kevin Osborn for the following line
# ----------------------------------
AddLabel(yes,
if (GetSymbol() == "/6S"
or GetSymbol() == "/ZB"
or GetSymbol() == "/ZC"
or GetSymbol() == "/NG"
or GetSymbol() == "/ZS"
or GetSymbol() == "/KC")
then "* ETF based"
else ""
, Color.YELLOW);
# display regular implied volatility
# ----------------------------------
#Plot IV
def ivs = Round(iv_data * 100, 2);
plot IV = ivs;
IV.SetLineWeight(1);
IV.SetStyle(Curve.POINTS);
#Plot IV High and Low Threshold
plot IVLow = IV_Threshold_Low;
IVLow.SetLineWeight(1);
IVLow.SetDefaultColor(color = Color.LIGHT_RED);
IVLow.Hide();
Plot IVHigh = IV_Threshold_High;
IVHigh.SetLineWeight(1);
IVHigh.SetDefaultColor(color = Color.LIGHT_GREEN);
IVHigh.Hide();
AddCloud(if IV_Show_High_Area then IVLow else double.nan, IVHigh, Color.GRAY, Color.Light_GREEN);
def low_over_timespan = LowestAll(ivs);
def high_over_timespan = HighestAll(ivs);
AddLabel(IV_Show_All_Labels, "IV MIN: " + Round(low_over_timespan,0) + "%", GlobalColor("IV-Color-LOW"));
#AddLabel(yes, "IV AVG(" + IV_Year_Range + "): " + round(Average(iv_data, IV_Year_Range) * 100,0) + "%", GlobalColor("IV-Color-AVG"));
#AddLabel(yes, "IV AVG(" + IV_2Quarter_Range + "): " + round(Average(iv_data, IV_2Quarter_Range) * 100,0) + "%", GlobalColor("IV-Color-AVG"));
#AddLabel(yes, "IV AVG(" + IV_Quarter_Range + "): " + round(Average(iv_data, IV_Quarter_Range) * 100,0) + "%", GlobalColor("IV-Color-AVG"));
#AddLabel(yes, "IV AVG(" + IV_Month_Range + "): " + round(Average(iv_data, IV_Month_Range) * 100,0) + "%", GlobalColor("IV-Color-AVG"));
AddLabel(IV_Show_All_Labels, "IV MAX: " + Round(high_over_timespan,0) + "%", GlobalColor("IV-Color-HIGH"));
AddLabel(yes, "IV: " + Round(ivs,0) + "%", if ivs > IV_Threshold_High then GlobalColor("IV-Color-HIGH") else (if ivs < IV_Threshold_Low then GlobalColor("IV-Color-LOW") else (if IsNaN(ivs) then Color.GRAY else GlobalColor("IV-Color-AVG"))));
# -----------------------------------
# IV Rank
# -----------------------------------
def ivr = Round( (ivs - low_over_timespan) / (high_over_timespan - low_over_timespan) * 100.0, 0);
IV.AssignValueColor(if ivr > IV_Threshold_HIGH then GlobalColor("IV-Color-HIGH") else (if ivr <= IV_Threshold_LOW then GlobalColor("IV-COLOR-LOW") else GlobalColor("IV-COLOR-AVG")));
AddLabel(yes, "IVR: " + ivr + "%", if ivr > IV_Threshold_High then GlobalColor("IV-COLOR-HIGH") else (if ivr < IV_Threshold_Low then GlobalColor("IV-COLOR-LOW") else GlobalColor("IV-COLOR-AVG")));
# -----------------------------------
# IV Percentile over different timeframes
# -----------------------------------
# how many times over the past year, has IV been below the current IV
def counts_below_y = fold py = 1 to IV_Year_Trading_Days + 1 with county = 0
do
if !IsNaN(GetValue(ivs, py)) and GetValue(ivs, py) <= ivs then
county + 1
else
county;
def ivp_year = if !isNaN(counts_below_y) then Round(counts_below_y / IV_Year_Trading_Days * 100.0, 0) else Double.NaN;
AddLabel(!isNaN(counts_below_y), "IVP: " + ivp_year + "%", if ivp_year > IV_Threshold_High then GlobalColor("IV-COLOR-HIGH") else (if ivp_year < IV_Threshold_Low then GlobalColor("IV-COLOR-LOW") else GlobalColor("IV-COLOR-AVG")));
def counts_below_2q = fold p2q = 1 to IV_HalfYear_Trading_Days + 1 with count2q = 0
do
if !IsNaN(GetValue(ivs, p2q)) and GetValue(ivs, p2q) <= ivs then
count2q + 1
else
count2q;
def ivp_2q = if !isNaN(counts_below_2q) then Round(counts_below_2q / IV_HalfYear_Trading_Days * 100.0, 0) else Double.NaN;
AddLabel(!isNaN(counts_below_2q) and IV_Show_All_Labels, "IVP(6M): " + ivp_2q + "%", if ivp_2q > IV_Threshold_High then GlobalColor("IV-COLOR-HIGH") else (if ivp_2q < IV_Threshold_Low then GlobalColor("IV-COLOR-LOW") else GlobalColor("IV-COLOR-AVG")));
def counts_below_q = fold pq = 1 to IV_Quarter_Trading_Days + 1 with countq = 0
do
if !IsNaN(GetValue(ivs, pq)) and GetValue(ivs, pq) <= ivs then
countq + 1
else
countq;
def ivp_q = if !isNaN(counts_below_q) then Round(counts_below_q / IV_Quarter_Trading_Days * 100.0, 0) else Double.NaN;
AddLabel(!isNaN(counts_below_q) and IV_Show_All_Labels, "IVP(3M): " + ivp_q + "%", if ivp_q > IV_Threshold_High then GlobalColor("IV-COLOR-HIGH") else (if ivp_q < IV_Threshold_Low then GlobalColor("IV-COLOR-LOW") else GlobalColor("IV-COLOR-AVG")));
def counts_below_m = fold pm = 1 to IV_Month_Trading_Days + 1 with countm = 0
do
if !IsNaN(GetValue(ivs, pm)) and GetValue(ivs, pm) <= ivs then
countm + 1
else
countm;
def ivp_m = if !isNaN(counts_below_m) then Round(counts_below_m / IV_Month_Trading_Days * 100.0, 0) else Double.NaN;
AddLabel(!isNaN(counts_below_m) and IV_Show_All_Labels, "IVP(M): " + ivp_m + "%", if ivp_m > IV_Threshold_High then GlobalColor("IV-COLOR-HIGH") else (if ivp_m < IV_Threshold_Low then GlobalColor("IV-COLOR-LOW") else GlobalColor("IV-COLOR-AVG")));
# ------------------------END ABOVE THIS LINE--------------------------