VIX Term Structure Indicator for ThinkorSwim

reknirt

New member
I'm trying to write an indicator plotting percent contango between spot VIX -- last closing VIX price -- and the near month /VX future. I got the idea from an indicator Jeff Augen did.

The indicator starts about 11:10


In theory the indicator should follow the S&P very closely -- at least until backwardation occurs and the near month contract becomes more expensive than spot VIX.

However, my indicator seems to be very noisy, whereas Augen's follows the S&P extremely closely. My formula for calculating the difference is:

contangoPercent = (NearContract - SpotVIX) / SpotVIX;

I corrected some of the noise by filtering out NaNs and replacing them with the previous bar. But I'm still not sure if my calculation is correct. Here is my code:

Code:
def month = GetMonth();
def daysTillExp = Next3rdFriday(2) - 30;
def whichMonth = if daysTillExp > 4 then month
    else if daysTillExp < 5 and month == 12
        then 1
    else if daysTillExp < 5
     then month + 1
    else double.nan;

def Year = GetYear();
def Year_Ext = Year - 2000;
def agg = AggregationPeriod.day;

def SpotVIX = close("VIX");
def NearContract = if whichMonth == 1
                   then Close(symbol = "/VXF" + Year_Ext, period = agg)
                   else if whichMonth == 2
                   then Close(symbol = "/VXG" + Year_Ext, period = agg)
                   else if whichMonth == 3
                   then Close(symbol = "/VXH" + Year_Ext, period = agg)
                   else if whichMonth == 4
                   then Close(symbol = "/VXJ" + Year_Ext, period = agg)
                   else if whichMonth == 5
                   then Close(symbol = "/VXK" + Year_Ext, period = agg)
                   else if whichMonth == 6
                   then Close(symbol = "/VXM" + Year_Ext, period = agg)
                   else if whichMonth == 7
                   then Close(symbol = "/VXN" + Year_Ext, period = agg)
                   else if whichMonth == 8
                   then Close(symbol = "/VXQ" + Year_Ext, period = agg)
                   else if whichMonth == 9
                   then Close(symbol = "/VXU" + Year_Ext, period = agg)
                   else if whichMonth == 10
                   then Close(symbol = "/VXV" + Year_Ext, period = agg)
                   else if whichMonth == 11
                   then Close(symbol = "/VXX" + Year_Ext, period = agg)
                   else if whichMonth == 12
                   then Close(symbol = "/VXZ" + Year_Ext, period = agg)
                   else double.nan;

# if the calculations on any of the bar return NaN, use the previous bar.

def spotFiltered = if IsNaN(spotVIX) then  spotFiltered[1]  else SpotVIX;
def nearFiltered = if IsNaN(NearContract) then  nearFiltered[1]  else NearContract;

plot contangoPercent = simpleMovingAvg((nearFiltered - spotFiltered) / spotFiltered, 5, 0);
#plot contangoPercent = (NearContract - SpotVIX) / SpotVIX;


plot zeroLine = 0;

addLabel(yes, daysTillExp);
addLabel(yes, whichMonth);
addLabel(yes, Next3rdFriday(2));

addlabel(Close(symbol = "VIX") <= NearContract,"Contango", color.green);
addlabel(Close(symbol = "VIX") > NearContract,"Backwardation", color.red);

# End Code VIX Contango/Normal Backwardation

Here is what it currently looks like on an /ES chart

FzCbahe.png


I appreciate any help!
 
Solution
@reknirt theres no percent of contango, because contango is contango, its like saying its in order or not in order, theres no percent of the order, its either true or false, theres no percent of true or false, its in order or its not. however theres percent of the difference from the spot and the futures .

To help you out...
heres a code from Mobius and AlphaInvestor

you simply have to plug in the mathematical equation for the symbol you want to get the percent from and plug in the formula for percent.

plot percent = ((NearContract - SpotVIX) / SpotVIX)*100;

Code:
# VIX Contango / Normal Backwardation Labels

# Mobius

# Chat Room Request 12.29.2015



# 02/28/2016 - AlphaInvestor - added color coded contango / backwardation...

rbapf

New member
The following is a study which uses the VIX futures to calculate the M1/M2 contango. It appears to work most of the time except for the second half of each December. Can anyone see where I went wrong?

Code:
input January = "/VXF";
input February = "/VXG";
input March = "/VXH";
input April = "/VXJ";
input May = "/VXK";
input June = "/VXM";
input July = "/VXN";
input August = "/VXQ";
input September = "/VXU";
input October = "/VXV";
input November = "/VXX";
input December = "/VXZ";

def Year = GetYear();

def JanuaryLength = 31;
def FebruaryLength = if Year % 4 == 0 then 29 else 28;
def MarchLength = 31;
def AprilLength = 30;
def MayLength = 31;
def JuneLength = 30;
def JulyLength = 31;
def AugustLength = 31;
def SeptemberLength = 30;
def OctoberLength = 31;
def NovemberLength = 30;
def DecemberLength = 31;

def Day = GetDayOfMonth(GetYYYYMMDD());
def WeekDay = GetDayOfWeek(GetYYYYMMDD());
def Month = GetMonth();

def MonthLength = if Month == 1 then JanuaryLength else if Month == 2 then FebruaryLength else if Month == 3 then MarchLength else if Month == 4 then AprilLength else if Month == 5 then MayLength else if Month == 6 then JuneLength else if Month == 7 then JulyLength else if Month == 8 then AugustLength else if Month == 9 then SeptemberLength else if Month == 10 then OctoberLength else if Month == 11 then NovemberLength else DecemberLength;

def AdjMonth = if Month == Month[1] then if (Day > MonthLength - 16 and Day < MonthLength - 8 and WeekDay == 3) then if Month == 12 then 1 else Month + 1 else if AdjMonth[1] != Month then AdjMonth[1] else Month else Month;

def M1 = close(Concat(if AdjMonth == 1 then January else if AdjMonth == 2 then February else if AdjMonth == 3 then March else if AdjMonth == 4 then April else if AdjMonth == 5 then May else if AdjMonth == 6 then June else if AdjMonth == 7 then July else if AdjMonth == 8 then August else if AdjMonth == 9 then September else if AdjMonth == 10 then October else if AdjMonth == 11 then November else December, Year % 100));

def M2 = close(if AdjMonth == 12 then Concat(January, (Year % 100) + 1) else Concat(if AdjMonth == 1 then February else if AdjMonth == 2 then March else if AdjMonth == 3 then April else if AdjMonth == 4 then May else if AdjMonth == 5 then June else if AdjMonth == 6 then July else if AdjMonth == 7 then August else if AdjMonth == 8 then September else if AdjMonth == 9 then October else if AdjMonth == 10 then November else December, Year % 100));

plot Contango = M2 / M1 - 1;

plot zeros = 0;
 

XeoNoX

Well-known member
VIP
@reknirt theres no percent of contango, because contango is contango, its like saying its in order or not in order, theres no percent of the order, its either true or false, theres no percent of true or false, its in order or its not. however theres percent of the difference from the spot and the futures .

To help you out...
heres a code from Mobius and AlphaInvestor

you simply have to plug in the mathematical equation for the symbol you want to get the percent from and plug in the formula for percent.

plot percent = ((NearContract - SpotVIX) / SpotVIX)*100;

Code:
# VIX Contango / Normal Backwardation Labels

# Mobius

# Chat Room Request 12.29.2015



# 02/28/2016 - AlphaInvestor - added color coded contango / backwardation label

# 02/28/2016 - AlphaInvestor - converted from Oil to VIX

# 12/01/2019 - AI - changed to 2 year option string



def Month = GetMonth();

def Year = GetYear();

#def Year_Ext = Year - 2010;

def Year_Ext = Year - 2000;  # AI - Fix year for new symbol logic

def agg = AggregationPeriod.DAY;

def NextContract = if Month == 1

                   then close(symbol = "/VXJ" + Year_Ext, period = agg)

                   else if Month == 2

                   then close(symbol = "/VXK" + Year_Ext, period = agg)

                   else if Month == 3

                   then close(symbol = "/VXM" + Year_Ext, period = agg)

                   else if Month == 4

                   then close(symbol = "/VXN" + Year_Ext, period = agg)

                   else if Month == 5

                   then close(symbol = "/VXQ" + Year_Ext, period = agg)

                   else if Month == 6

                   then close(symbol = "/VXU" + Year_Ext, period = agg)

                   else if Month == 7

                   then close(symbol = "/VXV" + Year_Ext, period = agg)

                   else if Month == 8

                   then close(symbol = "/VXX" + Year_Ext, period = agg)

                   else if Month == 9

                   then close(symbol = "/VXZ" + Year_Ext, period = agg)

                   else if Month == 10

                   then close(symbol = "/VXF" + (Year_Ext + 1), period = agg)

                   else if Month == 11

                   then close(symbol = "/VXG" + (Year_Ext + 1), period = agg)

                   else if Month == 12

                   then close(symbol = "/VXH" + (Year_Ext + 1), period = agg)

                   else Double.NaN;

def Month6Contract = if Month == 1

                   then close(symbol = "/VXQ" + Year_Ext, period = agg)

                   else if Month == 2

                   then close(symbol = "/VXU" + Year_Ext, period = agg)

                   else if Month == 3

                   then close(symbol = "/VXV" + Year_Ext, period = agg)

                   else if Month == 4

                   then close(symbol = "/VXX" + Year_Ext, period = agg)

                   else if Month == 5

                   then close(symbol = "/VXZ" + Year_Ext, period = agg)

                   else if Month == 6

                   then close(symbol = "/VXF" + (Year_Ext + 1), period = agg)

                   else if Month == 7

                   then close(symbol = "/VXG" + (Year_Ext + 1), period = agg)

                   else if Month == 8

                   then close(symbol = "/VXH" + (Year_Ext + 1), period = agg)

                   else if Month == 9

                   then close(symbol = "/VXJ" + (Year_Ext + 1), period = agg)

                   else if Month == 10

                   then close(symbol = "/VXK" + (Year_Ext + 1), period = agg)

                   else if Month == 11

                   then close(symbol = "/VXM" + (Year_Ext + 1), period = agg)

                   else if Month == 12

                   then close(symbol = "/VXN" + (Year_Ext + 1), period = agg)

                   else Double.NaN;

def YearOutContract = if Month == 1

                   then close(symbol = "/VXF" + (Year_Ext + 1), period = agg)

                   else if Month == 2

                   then close(symbol = "/VXG" + (Year_Ext + 1), period = agg)

                   else if Month == 3

                   then close(symbol = "/VXH" + (Year_Ext + 1), period = agg)

                   else if Month == 4

                   then close(symbol = "/VXJ" + (Year_Ext + 1), period = agg)

                   else if Month == 5

                   then close(symbol = "/VXK" + (Year_Ext + 1), period = agg)

                   else if Month == 6

                   then close(symbol = "/VXM" + (Year_Ext + 1), period = agg)

                   else if Month == 7

                   then close(symbol = "/VXN" + (Year_Ext + 1), period = agg)

                   else if Month == 8

                   then close(symbol = "/VXQ" + (Year_Ext + 1), period = agg)

                   else if Month == 9

                   then close(symbol = "/VXU" + (Year_Ext + 1), period = agg)

                   else if Month == 10

                   then close(symbol = "/VXV" + (Year_Ext + 1), period = agg)

                   else if Month == 11

                   then close(symbol = "/VXX" + (Year_Ext + 1), period = agg)

                   else if Month == 12

                   then close(symbol = "/VXZ" + (Year_Ext + 1), period = agg)

                   else Double.NaN;

AddLabel(1, "Date: " + Month +

            "/" +

             AsPrice(Year) +

          "  Current Contract: " +

             "/VX" +

          "  Curr Cont Price = " +

             AsDollars(close("/VX")) +

          "  Next Month Contract Price = " +

             AsDollars(NextContract) +

          "  6 Months Out = " +

             AsDollars(Month6Contract) +

          "  One Year Out = " +

             AsDollars(YearOutContract), Color.WHITE);



AddLabel(close("/VX") <= NextContract, "Contango", Color.GREEN);

AddLabel(close("/VX") > NextContract, "Backwardation", Color.RED);



AddLabel(close("/VX") <= NextContract

         and NextContract <= Month6Contract, " /VX Typical ", Color.GREEN);

AddLabel(close("/VX") > NextContract, " /VX Front Month Inverted ", Color.RED);

AddLabel(NextContract > Month6Contract, " /VX Back Month Inverted ", Color.RED);




# End Code VIX Contango/Normal Backwardation

Here is another one for you to try:

Code:
# Archive Name: RiskParityLiquidation_v2020_05_05_JQ
# Archive Tab: Volatility
#

#    Theory:  This script was composed based on observation of historical liquidation events.
#             In particular it relies on observations of the Dec 2018 and March 2020 liquidation events.
#             It is not going to be perfect but may pffer a reasonable heads up.
#             The red verticals indicate days on which the True Range exceeds the
#             Generalized 1 Month Implied Volatility.
#             The Orange Squares indicate an inverstion of the term structure of the
#             VIX futures contracts and the severity thereof.
#             Good Hunting  JQ
#             ps.  I would still prefer 1 bar vertical clouds to vertical lines but maybe tomorrow.

#    ATR_HistogramV6_JQ
#    2018-04-08 Version 2 - added label code from BLT
#    2018-04-10 Version 2 - added earnings dateline code from mobius          
#    2018-04-10 Version 2.1 - added Beta and R2 code from mobius  
#    2018-04-10 Version 2.2 - added ToS chart period Beta Code  
#    2018-04-10 Version 2.3 - added IV label from mobius
#    2018-04-10 Version 2.3 - added IV label from mobius
#    2018-04-10 Version 3 - added display querry code
#    2018-04-11 Version 5 -  added overnight indicator
#    2018-09-08 Version 7 -  added colors to overnight range to identify direction in overnight session
#    2020-03-26 converted to current naming convention
#    2020-03-26b converted to Risk Parity Alert Histo model
#    2020-03-31 added closeVX definition to reduce data calls

declare lower;
declare hide_on_intraday;

# inputs and variables
input visualAlerts = { default "Display", "Hide"};
addlabel (1, "Visual Alerts: " + visualAlerts + " ", if visualalerts then color.Magenta else Color.GRAY );
input lengthATR_RTH = 14; #Hint length: Default is 14
input lengthATR_Overnight = 14; #Hint length: Default is 14  JQ default 9
input averageType = AverageType.wilders;  #Hint averageType: Default is Wilders
input TrueRangeLabels = no;
input ImpliedMoveLabels = no;
input displayEarningsLines = yes;
input showDebugLabels = no;

def vOpen = Open;
def vHigh = High;
def vLow = Low;
def vClose = Close;

plot GlobexATR;
plot ATR;
plot LeastSquaresATR;
plot ImpliedMove_30;
plot Series1ImpliedMove;
plot GlobexRangeHisto;
plot TrueRangeHisto;



# GLOBEX RANGE
#Plot
GlobexRangeHisto = absValue(vopen - vclose[1]);
GlobexRangeHisto.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
GlobexRangeHisto.AssignValueColor(if vOpen > vclose[1]
                                  then(color.LIGHT_GREEN)
                                  else if vOpen < vclose[1]
                                     then (color.PINK)
                                  else (color.WHITE));
GlobexRangeHisto.HideBubble();
GlobexRangeHisto.HideTitle();


# GLOBEX AVERAGE TRUE RANGE
# Plot
GlobexATR = MovingAverage(averageType, GlobexRangeHisto, lengthATR_OVernight);
GlobexATR.setdefaultcolor(Color.white);
GlobexATR.setLineWeight(1);
GlobexATR.setHiding(1);

addlabel (TrueRangeLabels && averageType == averageType.WILDERS, "Globex ATRwilders" + lengthATR_Overnight + ": " + asdollars(GlobexATR) + " ", GlobexATR.TakeValueColor());
addlabel (TrueRangeLabels && averageType == averageType.EXPONENTIAL, "Globex ATRexponential" + lengthATR_Overnight + ": " + asdollars(GlobexATR) + " ", GlobexATR.TakeValueColor());
addlabel (TrueRangeLabels && averageType == averageType.SIMPLE, "Globex ATRsimple" + lengthATR_Overnight + ": " + asdollars(GlobexATR) + " ", GlobexATR.TakeValueColor());
addlabel (TrueRangeLabels && averageType == averageType.HULL, "Globex ATRhull" + lengthATR_Overnight + ": " + asdollars(GlobexATR) + " ", GlobexATR.TakeValueColor());
addlabel (TrueRangeLabels && averageType == averageType.WEIGHTED, "Globex ATRweighted" + lengthATR_Overnight + ": " + asdollars(GlobexATR) + " ", GlobexATR.TakeValueColor());





# TRUE RANGE HISTOGRAM
defineGlobalColor("UpColor", createcolor( 0, 170, 0));
defineGlobalColor("DnColor", createcolor( 240, 0, 0));
# Plot
TrueRangeHisto = TrueRange(vhigh, vclose, vlow);
TrueRangeHisto.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
TrueRangeHisto.AssignValueColor(if vclose > vclose[1]
                                then GlobalColor("upColor")
                                else if vclose < vclose[1]
                                   then GlobalColor("DnColor")
                                else (color.yellow));
TrueRangeHisto.HideBubble();
#TrueRangeHisto.HideTitle();



# Average True Range Code
Input AverageTrueRange = { default "Hide" , "Display"};
ATR = if AverageTrueRange
      then MovingAverage(averageType, TrueRange(vhigh, vclose, vlow), lengthATR_RTH)
      else Double.nan;
ATR.setDefaultColor(color.yellow);
ATR.setLineWeight(1);


addlabel (TrueRangeLabels && AverageTrueRange && averageType == averageType.WILDERS, "ATRwilders" + lengthATR_RTH +  ": " +  asdollars(ATR) + " " , atr.takeValueColor());
addlabel (TrueRangeLabels && AverageTrueRange && averageType == averageType.EXPONENTIAL, "ATRexponential" + lengthATR_RTH +  ": " +  asdollars(ATR) , atr.takeValueColor());
addlabel (TrueRangeLabels && AverageTrueRange && averageType == averageType.SIMPLE, "ATRsimple" + lengthATR_RTH +  ": " +  asdollars(ATR) , atr.takeValueColor());
addlabel (TrueRangeLabels && AverageTrueRange && averageType == averageType.HULL, "ATRhull" + lengthATR_RTH +  ": " +  asdollars(ATR) , atr.takeValueColor());
addlabel (TrueRangeLabels && AverageTrueRange && averageType == averageType.WEIGHTED, "ATRweighted" + lengthATR_RTH +  ": " +  asdollars(ATR) , atr.takeValueColor());


Input LeastSquares = { default "Hide" , "Display"};
Input LeastSquaresLength = 63;
# Plot
LeastSquaresATR = if LeastSquares
                  then inertia(TrueRangeHisto, LeastSquaresLength)
                  else Double.Nan;
LeastSquaresATR.SetPaintingStrategy(PaintingStrategy.LINE);
LeastSquaresATR.SetDefaultColor(Color.CYAN);
addlabel ( TrueRangeLabels and LeastSquares, "Least Squares ATR" + LeastSquaresLength + ": " + LeastSquaresATR + " ", LeastSquaresATR.takeValueColor() );


#    ImpVolatility
#    Description
#The Implied Volatility study is calculated using approximation method based on the Bjerksund-Stensland model. This model is usually employed for pricing American options on stocks, futures, and currencies; it is based on an exercise strategy corresponding to a flat boundary.
# IV and Implied Move Approximation Label
# Mobius

Input ImpliedVolatilityFunction = { default "Hide" , "Display"};
def tosImp_VolFunction = if ImpliedVolatilityFunction
                         then if IsNaN(Imp_Volatility(period = AggregationPeriod.DAY))
                              then tosImp_VolFunction[1]
                              else Imp_Volatility(period = AggregationPeriod.DAY)
                         else Double.nan;
plot ImpliedMove = (vopen * tosImp_VolFunction) / Sqrt(365);  #was vOpen
        impliedmove.setdefaultcolor(color.cyan);
        impliedmove.setlineweight(1);
AddLabel(ImpliedMoveLabels and ImpliedVolatilityFunction
            , " IV: " + AsPercent(tosImp_VolFunction)
                + "  Implied Move = "
                + AsDollars(ImpliedMove)
                + " "
            , impliedmove.takevaluecolor());



# This portion based on  --  significant changes were made though
# Calculating Generalizd Implied Volatility for 1 month
# Mobius
# V01.01

input Series_1IV = { default "Hide", "Display"};
input n = 252;
def c = close(period = "DAY");
def TOS_SIV = seriesVolatility(series = 1);
#Plot
Series1ImpliedMove  = if Series_1IV
                      then c * tos_Siv / Sqrt(365)
                      else double.nan;
Series1ImpliedMove.setDefaultColor(Color.PINK);
AddLabel(ImpliedMoveLabels && Series_1IV, " tosSeries1 IV: " + AsPercent(TOS_SIV)
                                + "  Implied Move = "
                                + asDollars(Series1ImpliedMove)
                           , Series1ImpliedMove.takeValueColor());




# Generalizd Implied Volatility for 1 month
def StDevF = Sqrt((fold i = 0 to n
                   with s = 0
                   do s + Sqr(Average(c, n) - GetValue(c, i))) / n);
def mIV = Sqrt(21) / StDevF;
ImpliedMove_30  = round ( c * mIV / Sqrt(365), 2);
ImpliedMove_30.setDefaultColor(color.LIGHT_GRAY);
Addlabel (ImpliedMoveLabels, " General IV30: " + AsPercent(mIV) + "   Implied Move = " + asdollars(ImpliedMove_30) + " "
                , ImpliedMove_30.takeValueColor() );
addChartBubble ( barNumber() == 2, ImpliedMove_30, "Generalized \n1 Month IV", ImpliedMove_30.takeValueColor(), yes);

plot RedTriggerLine = ImpliedMove_30 * 2;
redtriggerline.setdefaultColor(color.RED);
# End Code

def yellowTrigger = if TrueRangeHisto >= (1.0 * ImpliedMove_30) then 1 else 0;
def redTrigger = if TrueRangeHisto >= (1.5 * ImpliedMove_30) then 1 else 0;


AddVerticalLine(visible = !visualAlerts && yellowtrigger
                and vclose < vclose[1]
                , text = " "
                , color = if redtrigger then color.red else color.yellow);



AddVerticalLine(visible = !visualAlerts && yellowtrigger
                and vclose[1] < vclose[2]
                , text = " "
                ,  color = if redtrigger then color.red else color.yellow);
#addchartbubble ( 1, 0, "I" + ( .97 * impliedMove_30) + "\nH" + TrueRangeHisto, color.WHITE, no);

addlabel ( TrueRangeHisto >= (1.75 * ImpliedMove_30) and vclose < vclose[1]
           or TrueRangeHisto[1] >= (1.75 * ImpliedMove_30[1]) and vclose[1] < vclose[2]
           or TrueRangeHisto[2] >= (1.75 * ImpliedMove_30[2]) and vclose[2] < vclose[3]
           , " Risk Partity Fund Liquidation Likely "
           , color.red);
         


# VixTermStructure_v2020_03_28_JQ
# This script relies almost entirely on
# VIX Contango / Normal Backwardation Labels
# Mobius
# Chat Room Request 12.29.2015

# 02/28/2016 - AlphaInvestor - added color coded contango / backwardation label
# 02/28/2016 - AlphaInvestor - converted from Oil to VIX
# 12/01/2019 - AI - changed to 2 year option string
# 03/28/2020 - AI - adjusted contract letters because they are different from /cl
# 03/29/2020 - JQ - added a bit

def Month = GetMonth();
def Year = GetYear();
#def Year_Ext = Year - 2010;
def Year_Ext = Year - 2000;  # AI - Fix year for new symbol logic
def agg = AggregationPeriod.DAY;
def NextContract = if Month == 1
                   then close(symbol = "/VXH" + Year_Ext, period = agg)
                   else if Month == 2
                   then close(symbol = "/VXJ" + Year_Ext, period = agg)
                   else if Month == 3
                   then close(symbol = "/VXK" + Year_Ext, period = agg)
                   else if Month == 4
                   then close(symbol = "/VXM" + Year_Ext, period = agg)
                   else if Month == 5
                   then close(symbol = "/VXN" + Year_Ext, period = agg)
                   else if Month == 6
                   then close(symbol = "/VXQ" + Year_Ext, period = agg)
                   else if Month == 7
                   then close(symbol = "/VXU" + Year_Ext, period = agg)
                   else if Month == 8
                   then close(symbol = "/VXV" + Year_Ext, period = agg)
                   else if Month == 9
                   then close(symbol = "/VXX" + Year_Ext, period = agg)
                   else if Month == 10
                   then close(symbol = "/VXZ" + (Year_Ext + 1), period = agg)
                   else if Month == 11
                   then close(symbol = "/VXF" + (Year_Ext + 1), period = agg)
                   else if Month == 12
                   then close(symbol = "/VXG" + (Year_Ext + 1), period = agg)
                   else Double.NaN;

def Month2Contract = if Month == 1
                   then close(symbol = "/VXJ" + Year_Ext, period = agg)
                   else if Month == 2
                   then close(symbol = "/VXK" + Year_Ext, period = agg)
                   else if Month == 3
                   then close(symbol = "/VXM" + Year_Ext, period = agg)
                   else if Month == 4
                   then close(symbol = "/VXN" + Year_Ext, period = agg)
                   else if Month == 5
                   then close(symbol = "/VXQ" + Year_Ext, period = agg)
                   else if Month == 6
                   then close(symbol = "/VXU" + Year_Ext, period = agg)
                   else if Month == 7
                   then close(symbol = "/VXV" + Year_Ext, period = agg)
                   else if Month == 8
                   then close(symbol = "/VXX" + Year_Ext, period = agg)
                   else if Month == 9
                   then close(symbol = "/VXZ" + (Year_Ext + 1), period = agg)
                   else if Month == 10
                   then close(symbol = "/VXF" + (Year_Ext + 1), period = agg)
                   else if Month == 11
                   then close(symbol = "/VXG" + (Year_Ext + 1), period = agg)
                   else if Month == 12
                   then close(symbol = "/VXH" + (Year_Ext + 1), period = agg)
                   else Double.NaN;

def Month3Contract = if Month == 1
                   then close(symbol = "/VXK" + Year_Ext, period = agg)
                   else if Month == 2
                   then close(symbol = "/VXM" + Year_Ext, period = agg)
                   else if Month == 3
                   then close(symbol = "/VXN" + Year_Ext, period = agg)
                   else if Month == 4
                   then close(symbol = "/VXQ" + Year_Ext, period = agg)
                   else if Month == 5
                   then close(symbol = "/VXU" + Year_Ext, period = agg)
                   else if Month == 6
                   then close(symbol = "/VXV" + Year_Ext, period = agg)
                   else if Month == 7
                   then close(symbol = "/VXX" + Year_Ext, period = agg)
                   else if Month == 8
                   then close(symbol = "/VXZ" + (Year_Ext + 1), period = agg)
                   else if Month == 9
                   then close(symbol = "/VXF" + (Year_Ext + 1), period = agg)
                   else if Month == 10
                   then close(symbol = "/VXG" + (Year_Ext + 1), period = agg)
                   else if Month == 11
                   then close(symbol = "/VXH" + (Year_Ext + 1), period = agg)
                   else if Month == 12
                   then close(symbol = "/VXJ" + (Year_Ext + 1), period = agg)
                   else Double.NaN;


def closeVX = close("/VX") ;
input ContractPriceLabel = { default "Hide" , "Display"};
AddLabel(ContractPriceLabel, "Date: " + Month +
            "/" +
             AsPrice(Year) +
#          "  Current: " +
#             "/VX" +
          "  Price = " +
             AsDollars(closeVX) +
          "  Next Month Price = " +
             AsDollars(NextContract) +
          "  2 Months Out = " +
             AsDollars(Month2Contract) +
          "  3 Months Out = " +
             AsDollars(Month3Contract), Color.WHITE);


def FrontMonthInverstion = closeVX > NextContract;
plot FrontMonthAlerts = if FrontMonthInverstion then HighestAll(TRueRangeHisto) + 2 else double.nan;
FrontMonthAlerts.setPaintingStrategy(PaintingStrategy.SQUARES);
FrontMonthAlerts.setdefaultColor(Color.LIGHT_ORANGE);
FrontMonthAlerts.HideBubble();
FrontMonthAlerts.HideTitle();

def Month2Inverstion = closeVX> Month2Contract;
plot Month2Alerts = if Month2Inverstion then HighestAll(TRueRangeHisto) + 3 else double.nan;
Month2Alerts.setPaintingStrategy(PaintingStrategy.SQUARES);
Month2Alerts.setdefaultColor(Color.LIGHT_ORANGE);
Month2Alerts.HideBubble();
Month2Alerts.HideTitle();

def Month3Inverstion = closeVX > Month3Contract;
plot Month3Alerts = if Month3Inverstion then HighestAll(TRueRangeHisto) + 4 else double.nan;
Month3Alerts.setPaintingStrategy(PaintingStrategy.SQUARES);
Month3Alerts.setdefaultColor(Color.LIGHT_ORANGE);
#( if Month3Inverstion then color.orange else color.cyan);
Month3Alerts.HideBubble();
Month3Alerts.HideTitle();

input InvertedTermStructure = { "Hide" , default "Display" };
#AddLabel(close("/VX") <= NextContract, "/VX Front Month Typical", Color.GREEN);
AddLabel(InvertedTermStructure and ( closeVX > NextContract ), "/VX Front Month Inverted", Color.LIGHT_ORANGE);
AddLabel(InvertedTermStructure and ( closeVX > Month2Contract ), " 2nd Month Inverted", Color.LIGHT_ORANGE);
AddLabel(InvertedTermStructure and ( closeVX > Month3Contract ), " 3rd Month Inverted", Color.LIGHT_ORANGE);
# End Code VIX Contango/Normal Backwardation



# AI Debug
input Debug = no;
AddLabel(Debug, "Month" + Month, Color.GRAY);
AddLabel(Debug, "Year_Ext" + Year_Ext, Color.GRAY);

plot C3 = if Debug == yes then Month3Contract else Double.NaN;
plot m = if Debug == yes then  Month else Double.NaN;
plot y = if Debug == yes then Year_Ext else Double.NaN;

AddLabel(Debug, "VXH20: " + close(symbol = "/VXH" + (Year_Ext + 1)), Color.GRAY);
AddLabel(Debug, "VXH20: " + close(symbol = "/VXH20"), Color.GRAY);
AddLabel(Debug, "1 yr out: " + close(symbol = "/VXZ" + (Year_Ext + 1)), Color.GRAY);
AddLabel(Debug, "1 yr out: " + close(symbol = "/VXZ" + (Year_Ext + 2)), Color.GRAY);


# That's all Folks!


#AddVerticalLine(visible = visualAlerts && TrueRangeHisto >= ImpliedMove_30 and vclose > vclose[1], text = " ", color = Color.GREEN);

#addcloud ( if TrueRangeHisto[1] >= ImpliedMove_30[1]  then double.POSITIVE_INFINITY else double.nan, if TrueRangeHisto >= 0  then double.NEGATIVE_INFINITY else double.nan, color.light_RED, color.LIGHT_RED);

#addcloud ( if TrueRangeHisto >= ImpliedMove_30 and vclose > vclose[1]  then double.POSITIVE_INFINITY else double.nan, #double.NEGATIVE_INFINITY, color.Green, color.GREEN);

kHrnoml.png
 
Last edited:
Solution

randomx

New member
@reknirt theres no percent of contango, because contango is contango, its like saying its in order or not in order, theres no percent of the order, its either true or false, theres no percent of true or false, its in order or its not. however theres percent of the difference from the spot and the futures .

1. what he meant was the differential between the 2nd month and the 1st month of the vix term structure expressed in percentage. that's the contango percentage that he was referring to. how much different, in percentage terms, is the 2nd month and the 1st month.
contango % = [ ( m2 / m1 ) – 1 ] * 100

2. however, the formula he presented was for the roll yield which is the differential between the spot vix and the 1st month of the vix futures.
roll yield % = [ ( m1 / spot vix) – 1 ] * 100

i hope this helps whoever is searching the topic. the information is relevant regardless of the status of the original member asking this question
 
Last edited:

MerryDay

Administrative
Staff member
Staff
VIP
Lifetime
1. what he meant was the differential between the 2nd month and the 1st month of the vix term structure expressed in percentage. that's the contango percentage that he was referring to. how much different, in percentage terms, is the 2nd month and the 1st month.
contango % = [ ( m2 / m1 ) – 1 ] * 100

2. however, the formula he presented was for the roll yield which is the differential between the spot vix and the 1st month of the vix futures.
roll yield % = [ ( m1 / spot vix) – 1 ] * 100

i hope this helps
Did you know that clicking on a member's avatar will allow you to see when a member was last seen on the uTS forum? @XeoNoX has not been seen in a while. :(
 

Sascha_R

New member
I tried to amend the above script to work on all future symbols dynamically. When I use getsymbol(0 function for example for /CL, it will return /CL:XNYM. Therefore, I can't use Getsymbol() + Month of Expiration to refer to the term structure.

Any idea to adjust the script in such a way that it works for all future symbols?
 

randomx

New member
I tried to amend the above script to work on all future symbols dynamically. When I use getsymbol(0 function for example for /CL, it will return /CL:XNYM. Therefore, I can't use Getsymbol() + Month of Expiration to refer to the term structure.

Any idea to adjust the script in such a way that it works for all future symbols?
all futures symbols seems a bit greedy. there's quite a few symbols out there in the futures universe. you need to narrow it down to your target symbol
 

Sascha_R

New member
all futures symbols seems a bit greedy. there's quite a few symbols out there in the futures universe. you need to narrow it down to your target symbol
I was able to do it for Gold (/GC) and Oil (/CL). I understood that I have to do it manually for each symbol by using input variable for each symbol. Another question is how I can get a counter for the number of days since the term structure has flipped from contango to backwardation and vice versa?

input oil = "/CL:XNYM";
input gold = "/GC:XCEC";

def test_oil = if getsymbol()==oil then 1 else 0;

def test_gold = if getsymbol()==gold then 1 else 0;


def Month = GetMonth();

def Year = GetYear();

#def Year_Ext = Year - 2010;

def Year_Ext = Year - 2000; # AI - Fix year for new symbol logic

def agg = AggregationPeriod.DAY;


def NextContract = if Month == 1

then close(symbol = if test_oil then "/CL" +"J"+Year_Ext else "/GC"+"J"+Year_Ext, period = agg)

else if Month == 2

then close(symbol = if test_oil then "/CL" +"K"+Year_Ext else "/GC"+"K"+Year_Ext, period = agg)

else if Month == 3

then close(symbol = if test_oil then "/CL" +"M"+Year_Ext else "/GC"+"M"+Year_Ext, period = agg)

else if Month == 4

then close(symbol = if test_oil then "/CL" +"N"+Year_Ext else "/GC"+"N"+Year_Ext, period = agg)

else if Month == 5

then close(symbol = if test_oil then "/CL" +"Q"+Year_Ext else "/GC"+"Q"+Year_Ext, period = agg)

else if Month == 6

then close(symbol = if test_oil then "/CL" +"U"+Year_Ext else "/GC"+"U"+Year_Ext, period = agg)

else if Month == 7

then close(symbol = if test_oil then "/CL" +"V"+Year_Ext else "/GC"+"V"+Year_Ext, period = agg)

else if Month == 8

then close(symbol = if test_oil then "/CL" +"X"+Year_Ext else "/GC"+"X"+Year_Ext, period = agg)

else if Month == 9

then close(symbol = if test_oil then "/CL" +"Z"+Year_Ext else "/GC"+"Z"+Year_Ext, period = agg)

else if Month == 10

then close(symbol = if test_oil then "/CL" +"F"+(Year_Ext+1) else "/GC"+"F"+(Year_Ext+1), period = agg)

else if Month == 11

then close(symbol = if test_oil then "/CL" +"G"+(Year_Ext+1) else "/GC"+"G"+(Year_Ext+1), period = agg)

else if Month == 12

then close(symbol = if test_oil then "/CL" +"H"+(Year_Ext+1) else "/GC"+"H"+(Year_Ext+1), period = agg)

else Double.NaN;

def Month6Contract = if Month == 1

then close(symbol = if test_oil then "/CL" +"Q"+Year_Ext else "/GC"+"Q"+Year_Ext, period = agg)

else if Month == 2

then close(symbol = if test_oil then "/CL" +"U"+Year_Ext else "/GC"+"U"+Year_Ext, period = agg)

else if Month == 3

then close(symbol = if test_oil then "/CL" +"V"+Year_Ext else "/GC"+"V"+Year_Ext, period = agg)

else if Month == 4

then close(symbol = if test_oil then "/CL" +"X"+Year_Ext else "/GC"+"x"+Year_Ext, period = agg)

else if Month == 5

then close(symbol = if test_oil then "/CL" +"Z"+Year_Ext else "/GC"+"Z"+Year_Ext, period = agg)

else if Month == 6

then close(symbol = if test_oil then "/CL" +"F"+(Year_Ext+1) else "/GC"+"F"+(Year_Ext+1), period = agg)

else if Month == 7

then close(symbol = if test_oil then "/CL" +"G"+(Year_Ext+1) else "/GC"+"G"+(Year_Ext+1), period = agg)

else if Month == 8

then close(symbol = if test_oil then "/CL" +"H"+(Year_Ext+1) else "/GC"+"H"+(Year_Ext+1), period = agg)

else if Month == 9

then close(symbol = if test_oil then "/CL" +"J"+(Year_Ext+1) else "/GC"+"J"+(Year_Ext+1), period = agg)

else if Month == 10

then close(symbol = if test_oil then "/CL" +"K"+(Year_Ext+1) else "/GC"+"K"+(Year_Ext+1), period = agg)

else if Month == 11

then close(symbol = if test_oil then "/CL" +"M"+(Year_Ext+1) else "/GC"+"M"+(Year_Ext+1), period = agg)

else if Month == 12

then close(symbol = if test_oil then "/CL" +"N"+(Year_Ext+1) else "/GC"+"N"+(Year_Ext+1), period = agg)

else Double.NaN;

def YearOutContract = if Month == 1

then close(symbol = if test_oil then "/CL" +"F"+(Year_Ext+1) else "/GC"+"F"+(Year_Ext+1), period = agg)

else if Month == 2

then close(symbol = if test_oil then "/CL" +"G"+(Year_Ext+1) else "/GC"+"G"+(Year_Ext+1), period = agg)

else if Month == 3

then close(symbol = if test_oil then "/CL" +"H"+(Year_Ext+1) else "/GC"+"H"+(Year_Ext+1), period = agg)

else if Month == 4

then close(symbol =if test_oil then "/CL" +"J"+(Year_Ext+1) else "/GC"+"J"+(Year_Ext+1), period = agg)

else if Month == 5

then close(symbol = if test_oil then "/CL" +"K"+(Year_Ext+1) else "/GC"+"K"+(Year_Ext+1), period = agg)

else if Month == 6

then close(symbol =if test_oil then "/CL" +"M"+(Year_Ext+1) else "/GC"+"M"+(Year_Ext+1), period = agg)

else if Month == 7

then close(symbol =if test_oil then "/CL" +"N"+(Year_Ext+1) else "/GC"+"N"+(Year_Ext+1), period = agg)

else if Month == 8

then close(symbol = if test_oil then "/CL" +"Q"+(Year_Ext+1) else "/GC"+"Q"+(Year_Ext+1), period = agg)

else if Month == 9

then close(symbol = if test_oil then "/CL" +"U"+(Year_Ext+1) else "/GC"+"U"+(Year_Ext+1), period = agg)

else if Month == 10

then close(symbol = if test_oil then "/CL" +"V"+(Year_Ext+1) else "/GC"+"V"+(Year_Ext+1), period = agg)

else if Month == 11

then close(symbol = if test_oil then "/CL" +"X"+(Year_Ext+1) else "/GC"+"X"+(Year_Ext+1), period = agg)

else if Month == 12

then close(symbol = if test_oil then "/CL" +"Z"+(Year_Ext+1) else "/GC"+"Z"+(Year_Ext+1), period = agg)

else Double.NaN;


AddLabel(close(GetSymbol()) <= NextContract, "Front Month: " + AsDollars(NextContract) + "/ 6 Months: " + AsDollars(Month6Contract) + "/ Contango", Color.RED);

AddLabel(close(GetSymbol()) > NextContract, "Front Month: " + AsDollars(NextContract) + "/ 6 Months: " + AsDollars(Month6Contract) + "/ Backwardation", Color.GREEN);
 
@reknirt
Thanks for the code.

I've modified it to create the vix inversion curve as described in the following article. Any comments are welcome.

VIX Curve Inverts in Time-Honored Bull Signal Tied to Peak Panic

"As the S&P 500 suffered its worst week since October 2020, traders are paying up for near-term protection. The Cboe Volatility Index, a gauge of option costs, has surged 7 points to 26 over the span, pushing its spot price above that of its three-month futures for the first time in more than a month.
Such an inverted curve has occurred four other times in the past year and all coincided with market bottoms. "

XfYj7nY.png


Code:
# SMO_VIXInversion.ts
# Version 1.0, 01-23-2022
# Modified from
# https://usethinkscript.com/threads/vix-contango-indicator-for-thinkorswim.2068/

# Futures 3 months away
def monAhead = 3;
def monBack  = 13 - monAhead;

def month = GetMonth();
def whichMonth = if month < monBack then month + monAhead else month - monBack - 1;

def Year = GetYear();
def Year_Ext = Year - 2000;
def agg = AggregationPeriod.day;

def SpotVIX = close("VIX");
def NearContract = if whichMonth == 1
                   then Close(symbol = "/VXF" + Year_Ext, period = agg)
                   else if whichMonth == 2
                   then Close(symbol = "/VXG" + Year_Ext, period = agg)
                   else if whichMonth == 3
                   then Close(symbol = "/VXH" + Year_Ext, period = agg)
                   else if whichMonth == 4
                   then Close(symbol = "/VXJ" + Year_Ext, period = agg)
                   else if whichMonth == 5
                   then Close(symbol = "/VXK" + Year_Ext, period = agg)
                   else if whichMonth == 6
                   then Close(symbol = "/VXM" + Year_Ext, period = agg)
                   else if whichMonth == 7
                   then Close(symbol = "/VXN" + Year_Ext, period = agg)
                   else if whichMonth == 8
                   then Close(symbol = "/VXQ" + Year_Ext, period = agg)
                   else if whichMonth == 9
                   then Close(symbol = "/VXU" + Year_Ext, period = agg)
                   else if whichMonth == 10
                   then Close(symbol = "/VXV" + Year_Ext, period = agg)
                   else if whichMonth == 11
                   then Close(symbol = "/VXX" + Year_Ext, period = agg)
                   else if whichMonth == 12
                   then Close(symbol = "/VXZ" + Year_Ext, period = agg)
                   else double.nan;

# if the calculations on any of the bar return NaN, use the previous bar.
def spotFiltered = if IsNaN(spotVIX) then  spotFiltered[1]  else SpotVIX;
def nearFiltered = if IsNaN(NearContract) then  nearFiltered[1]  else NearContract;

# may use MA to smooth curve
#plot contangoPercent = simpleMovingAvg((-nearFiltered + spotFiltered) / spotFiltered, 4, 0);
plot vixInversion = (-nearFiltered + spotFiltered);
plot zeroLine = 0;

addLabel(yes, "Futures Month:"+whichMonth);

addlabel(vixInversion <= 0,"Contango", color.green);
addlabel(vixInversion > 0,"Inversion/Backwardation", color.red);
# End Code VIX Inversion/Backwardation
 

armdav

New member
@reknirt
Thanks for the code.

I've modified it to create the vix inversion curve as described in the following article. Any comments are welcome.

VIX Curve Inverts in Time-Honored Bull Signal Tied to Peak Panic

"As the S&P 500 suffered its worst week since October 2020, traders are paying up for near-term protection. The Cboe Volatility Index, a gauge of option costs, has surged 7 points to 26 over the span, pushing its spot price above that of its three-month futures for the first time in more than a month.
Such an inverted curve has occurred four other times in the past year and all coincided with market bottoms. "

XfYj7nY.png


Code:
# SMO_VIXInversion.ts
# Version 1.0, 01-23-2022
# Modified from
# https://usethinkscript.com/threads/vix-contango-indicator-for-thinkorswim.2068/

# Futures 3 months away
def monAhead = 3;
def monBack  = 13 - monAhead;

def month = GetMonth();
def whichMonth = if month < monBack then month + monAhead else month - monBack - 1;

def Year = GetYear();
def Year_Ext = Year - 2000;
def agg = AggregationPeriod.day;

def SpotVIX = close("VIX");
def NearContract = if whichMonth == 1
                   then Close(symbol = "/VXF" + Year_Ext, period = agg)
                   else if whichMonth == 2
                   then Close(symbol = "/VXG" + Year_Ext, period = agg)
                   else if whichMonth == 3
                   then Close(symbol = "/VXH" + Year_Ext, period = agg)
                   else if whichMonth == 4
                   then Close(symbol = "/VXJ" + Year_Ext, period = agg)
                   else if whichMonth == 5
                   then Close(symbol = "/VXK" + Year_Ext, period = agg)
                   else if whichMonth == 6
                   then Close(symbol = "/VXM" + Year_Ext, period = agg)
                   else if whichMonth == 7
                   then Close(symbol = "/VXN" + Year_Ext, period = agg)
                   else if whichMonth == 8
                   then Close(symbol = "/VXQ" + Year_Ext, period = agg)
                   else if whichMonth == 9
                   then Close(symbol = "/VXU" + Year_Ext, period = agg)
                   else if whichMonth == 10
                   then Close(symbol = "/VXV" + Year_Ext, period = agg)
                   else if whichMonth == 11
                   then Close(symbol = "/VXX" + Year_Ext, period = agg)
                   else if whichMonth == 12
                   then Close(symbol = "/VXZ" + Year_Ext, period = agg)
                   else double.nan;

# if the calculations on any of the bar return NaN, use the previous bar.
def spotFiltered = if IsNaN(spotVIX) then  spotFiltered[1]  else SpotVIX;
def nearFiltered = if IsNaN(NearContract) then  nearFiltered[1]  else NearContract;

# may use MA to smooth curve
#plot contangoPercent = simpleMovingAvg((-nearFiltered + spotFiltered) / spotFiltered, 4, 0);
plot vixInversion = (-nearFiltered + spotFiltered);
plot zeroLine = 0;

addLabel(yes, "Futures Month:"+whichMonth);

addlabel(vixInversion <= 0,"Contango", color.green);
addlabel(vixInversion > 0,"Inversion/Backwardation", color.red);
# End Code VIX Inversion/Backwardation
do I need to change the def Year_Ext = Year - 2000; line to def Year_Ext = Year - 2022;?
I was just playing with this indicator and noticed when you change the year, the results are different
 
do I need to change the def Year_Ext = Year - 2000; line to def Year_Ext = Year - 2022;?
I was just playing with this indicator and noticed when you change the year, the results are different
The variable Year_Ext is used for futures symbols, which include digits like 22 for year 2022 and 21 for year 2021. So I believe we have to use 2000.
 

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.
Top