Option Heatmap and OI Strikes

so im really interested in how to get put/call options strike oi to show up like volume profile and the lower heat map similar to this example from @melarue on twitter. I have no idea how to even get started on something like this. Any hints or expert coders out there willing to reverse engineer this?

Ideally, the heat map portion could be shown on the upper chart as well, able to be mapped to strikes on today and future expirations as a dynamic color grid for daily, weekly, monthly options. Options codes should auto-generate.

this could be tweaked to show gamma levels as well -- lots of potential.

anyone up to the challenge? :unsure:

FOlIJqyVEAMJS7T
 

ziongotoptions

Active member
Here is an updated version.
I've massively simplified the fold logic in my opinion. (I think there was a bug in the old code anyways where I was adding ATM data twice)

I've also added gamma exposure based on the math here: https://perfiliev.co.uk/market-commentary/how-to-calculate-gamma-exposure-and-zero-gamma-level/
Although I'm not sure its correct, as the math is quite complicated, and my background is not in finance.
I'm also not sure how to interpret the data just yet ...
It doesn't make it any easier that I realized you can't call a custom script inside the fold using the index of the fold o_O :unsure:
As a workaround, I had to try and implement the formula for deriving the spot gamma in a single line within the fold, as part of the larger calculation for the spot gamma exposure at that level. This is the mobius Greeks formula (which uses Black Scholes), using the OptionPrice() and SeriesVolatility() as inputs.

Some Notes:
  1. Ideally to get the 'Total Market Gamma Exposure' we would need to perform this calculation on all strikes, for every expiry, for every day. I have only tested going +/- 5 to 10 strikes out, for weekly series so far, and it already takes quite a bit of time. For reference I'm running an i9-9900k with 64GB DDR4 RAM, Running this at 5 strike depth weekly on SPY is taking around 15 -20 minutes (hard to tell when its actually done running so I'm just going by when my pc fans/water cooler fans stop spinning at max rpm lol)
  2. For number 1 to work, we would need to further modify the date/strike logic to be able to pick up more than just next series expiries. I also don't know how this effects the SeriesVolatility function.
  3. Maybe another approach would be to modify this so that we can pick up the first couple close to the money strikes, and then skip to picking up a couple way out of the money strikes to get a better overall picture.
Screenshot:

pQI47if.png


Code:

Code:
#hint: Options Hacker \n This study lets you scan the options chain for a series and given depth. \n<b>Warning: Setting the StrikeDepth to large values requires significant processing power, and will result in slow loading times.</b>


#-----------------------------------------------------------------------------------------------------------------#
# Settings

# Colors
DefineGlobalColor("Call", Color.GREEN);
DefineGlobalColor("Put", Color.RED);
DefineGlobalColor("CallCloud",Color.DARK_GREEN);
DefineGlobalColor("PutCloud",Color.DARK_RED);

#hint Mode: The mode to select an option symbol. \n AUTO will try to find the option symbol based on the Series and StrikeDepth inputs. \n MANUAL allow an override of the AUTO behavior by using the ManualCenterStrike and ManualStrikeSpacing inputs to determine the option symbol.
input Mode = {default AUTO, MANUAL};

#hint Series: The option expiration series to search. \n This value is used to determine the option symbol.
input Series = {
    default Weekly,
    Month1,
    Month2,
    Month3,
    Month4,
    Month5,
    Month6,
    Month7,
    Month8,
    Month9
};

#hint DataType: The type of option data to show.
input DataType = {default OpenInterest, Volume, GammaExposure};

#hint StrikeDepth: The level of depth to search a series. (+/- this far from ATM)
input StrikeDepth = 10;

#hint CenterStrikeOffset: The offset to use when calculating the center strike based on close price. \n Examples: \n   1 = nearest $1 interval \n   10 = nearest $10 interval.
input CenterStrikeOffset = 1.0;

#hint MaxStrikeSpacing: The maximum dollar amount between two adjacent contracts.
input MaxStrikeSpacing = 25;

#hint ManualCenterStrike: The starting price to use when in MANUAL mode.
input ManualCenterStrike = 440;

#hint ManualStrikeSpacing: The dollar amount between two adjacent contracts to use when in MANUAL mode.
input ManualStrikeSpacing = 1.0;

#hint ShowStrikeInfo: Show the strike info labels.
input ShowStrikeInfo = yes;

#hint ShowLabels: Show the open interest labels.
input ShowLabels = yes;

#hint ShowClouds: Show the open interest clouds.
input ShowClouds = no;

#hint ShowLines: Show the open interest lines.
input ShowLines = yes;

#hint ShowGreeks: Show the estimated Greek calculation labels for the latest bar.
input ShowGreeks = yes;


#-----------------------------------------------------------------------------------------------------------------#
# Date, Symbol, and Strike

# OptionSeries is the expiry starting at 1 and raising by one for each next expiry.
def OptionSeries;
switch (Series) {
    case Weekly:
        OptionSeries = 1;
    case Month1:
        OptionSeries = 2;
    case Month2:
        OptionSeries = 3;
    case Month3:
        OptionSeries = 4;
    case Month4:
        OptionSeries = 5;
    case Month5:
        OptionSeries = 6;
    case Month6:
        OptionSeries = 7;
    case Month7:
        OptionSeries = 8;
    case Month8:
        OptionSeries = 9;
    case Month9:
        OptionSeries = 10;
};

# Open price at Regular Trading Hours
def RTHopen = open(period = AggregationPeriod.DAY);

# Current year, month, day, and date
def CurrentYear = GetYear(); # number of current bar in CST
def CurrentMonth = GetMonth(); # 1 - 12
def CurrentDay = GetDay(); # 1 - 365 (366 for leap year)
def CurrentDate = GetYYYYMMDD(); # date of the current bar in the YYYYMMDD

# Current day of this month
def CurrentDayOfMonth = GetDayOfMonth(CurrentDate);

# Get the first day of this month - 1 (Monday) to 7 (Sunday)
def FirstDayThisMonth = GetDayOfWeek((CurrentYear * 10000) + (CurrentMonth * 100) + 1);

# Get the first upcoming friday
def FirstUpcomingFriday =
    if FirstDayThisMonth < 6 then 6 - FirstDayThisMonth
    else if FirstDayThisMonth == 6 then 7
    else 6
;

# Get the second, third, and fourth upcoming fridays
def SecondUpcomingFriday = FirstUpcomingFriday + 7;
def ThirdUpcomingFriday = FirstUpcomingFriday + 14;
def FourthUpcomingFriday = FirstUpcomingFriday + 21;

# Pick up all Fridays of the current month for weekly options
def RollDOM = FirstUpcomingFriday + 21;
def ExpMonth1 =
    if RollDOM > CurrentDayOfMonth then CurrentMonth + OptionSeries - 1
    else CurrentMonth + OptionSeries
;

# Options month input
def ExpMonth2 = if ExpMonth1 > 12 then ExpMonth1 - 12 else ExpMonth1;

# Options year input
def ExpYear = if ExpMonth1 > 12 then CurrentYear + 1 else CurrentYear;

# First friday expiry calc
def ExpDay1DOW = GetDayOfWeek(ExpYear * 10000 + ExpMonth2 * 100 + 1);
def ExpFirstFridayDOM =
    if ExpDay1DOW < 6 then 6 - ExpDay1DOW
    else if ExpDay1DOW == 6 then 7
    else 6
;

# Options code day of month input
def ExpDOM =
    if CurrentDayOfMonth < ExpFirstFridayDOM -1 then FirstUpcomingFriday
    else if between(CurrentDayOfMonth, ExpFirstFridayDOM, SecondUpcomingFriday - 1) then SecondUpcomingFriday
    else if between(CurrentDayOfMonth, SecondUpcomingFriday, ThirdUpcomingFriday - 1) then ThirdUpcomingFriday
    else if between(CurrentDayOfMonth, ThirdUpcomingFriday, FourthUpcomingFriday - 1) then FourthUpcomingFriday
    else ExpFirstFridayDOM
;

# Centerstrike
def CenterStrike =
    if (Mode == Mode.AUTO and !IsNaN(close)) then Round(close / CenterStrikeOffset, 0) * CenterStrikeOffset
    else if (Mode == Mode.MANUAL and !IsNaN(close)) then ManualCenterStrike
    else CenterStrike[1]
;

# This is still some voodoo to me ...
def OptionExpiryDate = ExpYear * 10000 + ExpMonth2 * 100 + ExpDOM + 1;

# Strike Spacing
def StrikeSpacingC =
    fold i = 1 to MaxStrikeSpacing
    with spacing = 0
    do if DataType == DataType.OpenInterest or DataType == DataType.GammaExposure and !IsNaN(
        open_interest(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "P" + AsPrice(CenterStrike + (MaxStrikeSpacing - i)))
    )
    then MaxStrikeSpacing - i
    else if DataType == DataType.Volume and !IsNaN(
        volume(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "P" + AsPrice(CenterStrike + (MaxStrikeSpacing - i)))
    )
    then MaxStrikeSpacing - i
    else spacing
;
def StrikeSpacing =
    if (Mode == Mode.AUTO and !IsNaN(close)) then StrikeSpacingC
    else if (Mode == Mode.MANUAL and !IsNaN(close)) then ManualStrikeSpacing
    else StrikeSpacing[1]
;

# Testing ---------
#plot Date = GetYYYYMMDD();
#plot Expiry = (OptionExpiryDate);
#plot DTE  = AbsValue(CountTradingDays(Date, Expiry));

#-----------------------------------------------------------------------------------------------------------------#
# Option Chain Data Gathering


# Total Put Open Interest for selected chain depth and expiry series
def TotalPutOpenInterest =
    fold poiIndex = -(StrikeDepth) to StrikeDepth
    with poi = 0
    do
        if DataType == DataType.OpenInterest and !IsNaN(
            open_interest(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "P" + AsPrice(CenterStrike + (StrikeSpacing * poiIndex)))
        )
        then poi + open_interest(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "P" + AsPrice(CenterStrike + (StrikeSpacing * poiIndex)))
        else 0
;


# Total Call Open Interest for selected chain depth and expiry series
def TotalCallOpenInterest =
    fold coiIndex = -(StrikeDepth) to StrikeDepth
    with coi = 0
    do
        if DataType == DataType.OpenInterest and !IsNaN(
            open_interest(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "C" + AsPrice(CenterStrike + (StrikeSpacing * coiIndex)))
        )
        then coi + open_interest(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "C" + AsPrice(CenterStrike + (StrikeSpacing * coiIndex)))
        else 0
;


# Total Put Volume for selected chain depth and expiry series
def TotalPutVolume =
    fold pvIndex = -(StrikeDepth) to StrikeDepth
    with pv = 0
    do
        if DataType == DataType.Volume and !IsNaN(
            volume(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "P" + AsPrice(CenterStrike + StrikeSpacing * pvIndex))
        )
        then pv + volume(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "P" + AsPrice(CenterStrike + StrikeSpacing * pvIndex))
        else 0
;


# Total Call Open Interest for selected chain depth and expiry series
def TotalCallVolume =
    fold cvIndex = -(StrikeDepth) to StrikeDepth
    with cv = 0
    do
        if DataType == DataType.Volume and !IsNaN(
            volume(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "C" + AsPrice(CenterStrike + StrikeSpacing * cvIndex))
        )
        then cv + volume(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "C" + AsPrice(CenterStrike + StrikeSpacing * cvIndex))
        else 0
;


#-----------------------------------------------------------------------------------------------------------------#
# Greeks Calculations
#
# K  - Option strike price
# N  - Standard normal cumulative distribution function
# r  - Risk free interest rate
# IV - Volatility of the underlying
# S  - Price of the underlying
# t  - Time to option's expiry
#
# d1    = (ln(S/K) + (r + (sqr(IV)/2))t) / (? (sqrt(t)))
# d2    = e -(sqr(d1) / 2) / sqrt(2*pi)
#
# Delta = N(d1)
# Gamma = (d2) / S(IV(sqrt(t)))
# Theta = ((S d2))IV) / 2 sqrt(t)) - (rK e(rt)N(d4))
#         where phi(d3) = (exp(-(sqr(x)/2))) / (2 * sqrt(t))
#         where d4 = d1 - IV(sqrt(t))
# Vega  = S phi(d1) Sqrt(t)


# TODO: Figure out how to dynamically get this value
def DayToExpiry = AbsValue(CountTradingDays(CurrentDate, OptionExpiryDate) - 1);

# Get the implied volatility for calculations
# Input: series is the expiry starting at 1 and raising by 1 for each next expiry
def IV = SeriesVolatility(series = OptionSeries);


def K = CenterStrike;
def S = close;


def r = GetInterestRate();
def t = (DayToExpiry / 365);
def d1 = (Log(S / K) + ((r + (Sqr(IV) / 2)) * t)) / (IV * Sqrt(t));
def d2 = Exp(-(Sqr(d1) / 2)) / Sqrt(2 * Double.Pi);
script N {
    input data  = 1;
    def a = AbsValue(data);
    def b1 =  .31938153;
    def b2 = -.356563782;
    def b3 = 1.781477937;
    def b4 = -1.821255978;
    def b5 = 1.330274429;
    def b6 =  .2316419;
    def e = 1 / (1 + b6 * a);
    def i = 1 - 1 / Sqrt(2 * Double.Pi) * Exp(-Power(a, 2) / 2) *
           (b1 * e + b2 * e * e + b3 *
            Power(e, 3) + b4 * Power(e, 4) + b5 * Power(e, 5));
    plot CND = if data < 0
               then 1 - i
               else i;
}


# TODO: These values don't quite line up
# My background on options pricing models is not very good

# Delta
def Delta = N(d1);

# Gamma
def Gamma = d2 / (S * (IV * Sqrt(t)));

# Theta
def Theta = -(-(S*d2*IV*(.5000)/
             (2*sqrt(t)))-
             (r*(exp(-r*t)*K))*N(d2)+(S*N(d1)*(.5000)))/365;
# (.5000) variant less than .5 e(X/t)

# Vega
def Vega = (S*d2*sqrt(t))/100;


script SpotGamma {
    input DTE = 0;
    input K = 0;
    input Series = 0;
    def S = close;
    def t = (DTE / 365);
    def IV = SeriesVolatility(series = Series);
    def d1 = (Log(S / K) + ((GetInterestRate() + (Sqr(IV) / 2)) * t)) / (IV * Sqrt(t));

    # Delta
    def Delta = N(d1);

    # Gamma
    def d2 = Exp(-(Sqr(d1) / 2)) / Sqrt(2 * Double.Pi);
    def Gamma = d2 / (S * (IV * Sqrt(t)));
   
    plot SpotGamma = Gamma;
}


# Total Put Gamma Exposure for selected chain depth and expiry series
def TotalPutGammaExposure =
    fold pgexIndex = -(StrikeDepth) to StrikeDepth
    with pgex = 0
    do
        if DataType == DataType.GammaExposure and !IsNaN(
            open_interest(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "P" + AsPrice(CenterStrike + (StrikeSpacing * pgexIndex)))
        )
        then pgex +
            open_interest(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "P" + AsPrice(CenterStrike + (StrikeSpacing * pgexIndex))) *
            Sqr(OptionPrice((CenterStrike + (StrikeSpacing * pgexIndex)), yes, AbsValue(CountTradingDays(CurrentDate, OptionExpiryDate) - 1), close, SeriesVolatility(series = OptionSeries), no, 0.0, GetInterestRate())) * 0.01 *
            #SpotGamma(AbsValue(CountTradingDays(CurrentDate, OptionExpiryDate) - 1), (CenterStrike + (StrikeSpacing * pgexIndex)), OptionSeries) * # Honestly why cant I put the index inside here :(
            (Exp(-(Sqr((Log(close / (CenterStrike + (StrikeSpacing * pgexIndex))) + ((GetInterestRate() + (Sqr(SeriesVolatility(series = OptionSeries)) / 2)) * (AbsValue(CountTradingDays(CurrentDate, OptionExpiryDate) - 1) / 365))) / (SeriesVolatility(series = OptionSeries) * Sqrt((AbsValue(CountTradingDays(CurrentDate, OptionExpiryDate) - 1) / 365)))) / 2)) / Sqrt(2 * Double.Pi) / (close * (SeriesVolatility(series = OptionSeries) * Sqrt((AbsValue(CountTradingDays(CurrentDate, OptionExpiryDate) - 1) / 365))))) *
            100 *
            -1
        else 0
;

# Total Call Gamma Exposure for selected chain depth and expiry series
def TotalCallGammaExposure =
    fold cgexIndex = -(StrikeDepth) to StrikeDepth
    with cgex = 0
    do
        if DataType == DataType.GammaExposure and !IsNaN(
            open_interest(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "C" + AsPrice(CenterStrike + (StrikeSpacing * cgexIndex)))
        )
        then cgex +
            open_interest(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "C" + AsPrice(CenterStrike + (StrikeSpacing * cgexIndex))) *
            Sqr(OptionPrice((CenterStrike + (StrikeSpacing * cgexIndex)), no, AbsValue(CountTradingDays(CurrentDate, OptionExpiryDate) - 1), close, SeriesVolatility(series = OptionSeries), no, 0.0, GetInterestRate())) * 0.01 *
            #SpotGamma(AbsValue(CountTradingDays(CurrentDate, OptionExpiryDate) - 1), (CenterStrike + (StrikeSpacing * 0)), OptionSeries) * # Honestly why cant I put the index inside here :(
            (Exp(-(Sqr((Log(close / (CenterStrike + (StrikeSpacing * cgexIndex))) + ((GetInterestRate() + (Sqr(SeriesVolatility(series = OptionSeries)) / 2)) * (AbsValue(CountTradingDays(CurrentDate, OptionExpiryDate) - 1) / 365))) / (SeriesVolatility(series = OptionSeries) * Sqrt((AbsValue(CountTradingDays(CurrentDate, OptionExpiryDate) - 1) / 365)))) / 2)) / Sqrt(2 * Double.Pi) / (close * (SeriesVolatility(series = OptionSeries) * Sqrt((AbsValue(CountTradingDays(CurrentDate, OptionExpiryDate) - 1) / 365))))) *
            100
        else 0
;

plot GammaExposure = TotalPutGammaExposure + TotalCallGammaExposure;


#-----------------------------------------------------------------------------------------------------------------#
# Visuals

# Selected DataType
AddLabel(yes, DataType, Color.LIGHT_GRAY);

# Selected Series
AddLabel(yes, Series, Color.LIGHT_GRAY);

# Center Strike Label
AddLabel(ShowStrikeInfo, "Center Strike: " + AsDollars(CenterStrike), Color.LIGHT_GRAY);

# Chain Depth Label
AddLabel(ShowStrikeInfo, "Strike Depth: +/-" + StrikeDepth, Color.LIGHT_GRAY);

# Strike Spacing Label
AddLabel(ShowStrikeInfo, "Strike Spacing: " + AsDollars(StrikeSpacing), Color.LIGHT_GRAY);

# Current ATM Options Labels
Addlabel(ShowStrikeInfo, "ATM Put: " + ("." + GetSymbol()) + AsPrice(OptionExpiryDate - 20000001) + "P" + AsPrice(CenterStrike), Color.LIGHT_RED);
Addlabel(ShowStrikeInfo, "ATM Call: " + ("." + GetSymbol()) + AsPrice(OptionExpiryDate - 20000001) + "C" + AsPrice(CenterStrike), Color.LIGHT_GREEN);

# Create a center line
plot ZeroLine = 0;
ZeroLine.SetDefaultColor(Color.WHITE);

# Call Open Interest
plot CallOpenInterest = TotalCallOpenInterest;
CallOpenInterest.SetHiding(!ShowLines and DataType == DataType.OpenInterest);
CallOpenInterest.SetPaintingStrategy(PaintingStrategy.LINE);
CallOpenInterest.SetDefaultColor(GlobalColor("Call"));
AddLabel(ShowLabels and DataType == DataType.OpenInterest, "CallOI: " + CallOpenInterest, GlobalColor("Call"));

# Put Open Interest
plot PutOpenInterest = -(TotalPutOpenInterest); # Make negative to flip under axis
PutOpenInterest.SetHiding(!ShowLines and DataType == DataType.OpenInterest);
PutOpenInterest.SetPaintingStrategy(PaintingStrategy.LINE);
PutOpenInterest.SetDefaultColor(GlobalColor("Put"));
AddLabel(ShowLabels and DataType == DataType.OpenInterest, "PutOI: " + TotalPutOpenInterest, GlobalColor("Put"));

# Create Clouds for Open Interest
AddCloud(
    if ShowClouds and DataType == DataType.OpenInterest then CallOpenInterest else Double.NaN,
    if ShowClouds and DataType == DataType.OpenInterest then Zeroline else Double.NaN,
    GlobalColor("CallCloud"), GlobalColor("PutCloud")
);
AddCloud(
    if ShowClouds and DataType == DataType.OpenInterest then Zeroline else Double.NaN,
    if ShowClouds and DataType == DataType.OpenInterest then PutOpenInterest else Double.NaN,
    GlobalColor("PutCloud"), GlobalColor("CallCloud")
);

# Hull Moving Average of Put Open Interest
plot PutOpenInterestAverage = hullmovingavg(PutOpenInterest);
PutOpenInterestAverage.SetHiding(!ShowLines and DataType == DataType.OpenInterest);
PutOpenInterestAverage.SetDefaultColor(Color.ORANGE);
PutOpenInterestAverage.SetStyle(Curve.MEDIUM_DASH);

# Hull Moving Average of Call Open Interest
plot CallOpenInterestAverage = hullmovingavg(CallOpenInterest);
CallOpenInterestAverage.SetHiding(!ShowLines and DataType == DataType.OpenInterest);
CallOpenInterestAverage.SetDefaultColor(Color.LIGHT_GREEN);
CallOpenInterestAverage.SetStyle(Curve.MEDIUM_DASH);

# Color Gradient of Total Average Open Interest
#plot TotalOpenInterestAverage = average(CallOpenInterest + PutOpenInterest);
#TotalOpenInterestAverage.SetHiding(!ShowLines and DataType == DataType.OpenInterest);
#TotalOpenInterestAverage.SetLineWeight(3);
#TotalOpenInterestAverage.DefineColor("Default", Color.YELLOW);
#TotalOpenInterestAverage.DefineColor("Highest", Color.GREEN);
#TotalOpenInterestAverage.DefineColor("Lowest", Color.RED);
#TotalOpenInterestAverage.AssignNormGradientColor(14, TotalOpenInterestAverage.Color("Lowest"), TotalOpenInterestAverage.Color("Highest"));
#AddCloud(
#    if ShowClouds and DataType == DataType.OpenInterest then TotalOpenInterestAverage else Double.NaN,
#    if ShowClouds and DataType == DataType.OpenInterest then Zeroline else Double.NaN,
#    color.GREEN, color.RED
#);

# Call Volume
plot CallVolume = TotalCallVolume;
CallVolume.SetHiding(!ShowLines and DataType == DataType.Volume);
CallVolume.SetPaintingStrategy(PaintingStrategy.LINE);
CallVolume.SetDefaultColor(GlobalColor("Call"));
AddLabel(ShowLabels and DataType == DataType.Volume, "CallVol: " + CallVolume, GlobalColor("Call"));

# Put Volume
plot PutVolume = -(TotalPutVolume); # Make negative to flip under axis
PutVolume.SetHiding(!ShowLines and DataType == DataType.Volume);
PutVolume.SetPaintingStrategy(PaintingStrategy.LINE);
PutVolume.SetDefaultColor(GlobalColor("Put"));
AddLabel(ShowLabels and DataType == DataType.Volume, "PutVol: " + TotalPutVolume, GlobalColor("Put"));

# Create Clouds for Volume
AddCloud(
    if ShowClouds and DataType == DataType.Volume then CallVolume else Double.NaN,
    if ShowClouds and DataType == DataType.Volume then Zeroline else Double.NaN,
    GlobalColor("CallCloud"), GlobalColor("PutCloud")
);
AddCloud(
    if ShowClouds and DataType == DataType.Volume then Zeroline else Double.NaN,
    if ShowClouds and DataType == DataType.Volume then PutVolume else Double.NaN,
    GlobalColor("PutCloud"), GlobalColor("CallCloud")
);

# Hull Moving Average of Put Volume
plot PutVolumeAverage = hullmovingavg(PutVolume);
PutVolumeAverage.SetHiding(!ShowLines and DataType == DataType.Volume);
PutVolumeAverage.SetDefaultColor(Color.ORANGE);
PutVolumeAverage.SetStyle(Curve.MEDIUM_DASH);

# Hull Moving Average of Call Volume
plot CallVolumeAverage = hullmovingavg(CallVolume);
CallVolumeAverage.SetHiding(!ShowLines and DataType == DataType.Volume);
CallVolumeAverage.SetDefaultColor(Color.LIGHT_GREEN);
CallVolumeAverage.SetStyle(Curve.MEDIUM_DASH);

# Color Gradient of Total Average Volume
#plot TotalVolumeAverage = average(CallVolume + PutVolume);
#TotalVolumeAverage.SetHiding(!ShowLines and DataType == DataType.Volume);
#TotalVolumeAverage.SetLineWeight(3);
#TotalVolumeAverage.DefineColor("Default", Color.YELLOW);
#TotalVolumeAverage.DefineColor("Highest", Color.GREEN);
#TotalVolumeAverage.DefineColor("Lowest", Color.RED);
#TotalVolumeAverage.AssignNormGradientColor(14, TotalVolumeAverage.Color("Lowest"), TotalVolumeAverage.Color("Highest"));
#AddCloud(
#    if ShowClouds and DataType == DataType.Volume then TotalVolumeAverage else Double.NaN,
#    if ShowClouds and DataType == DataType.Volume then Zeroline else Double.NaN,
#    Color.GREEN, Color.RED
#);

# Greeks Labels
AddLabel(ShowGreeks, "Delta: " + Delta, Color.WHITE);
AddLabel(ShowGreeks, "Gamma: " + Gamma, Color.WHITE);
AddLabel(ShowGreeks, "Theta: " + Theta, Color.WHITE);
AddLabel(ShowGreeks, "Vega: " + Vega, Color.WHITE);
the gamma exposure line is not plotting at all for me
 

ziongotoptions

Active member
Okay here's a question for the gurus.
According to https://perfiliev.co.uk/market-commentary/how-to-calculate-gamma-exposure-and-zero-gamma-level/


According to https://squeezemetrics.com/monitor/download/pdf/white_paper.pdf


The latest code is utilizing the the first approach, second formula to get the percent change.
Why does the second approach by squeezemetrics not use the spot price as well?
What does the 'Gamma Contribution' mean vs 'GEX in shares'?

From my understand of GEX as a concept:
when GEX > 0 = less volatility (MM's hedging by buying into lows, selling into highs)​
and conversely when GEX < 0 = greater volatility (MM's selling into lows, buying into highs thus fueling volatility)​
What approach would be the best to take here? I'll play with adding some switching logic so we can play with the different fo
it seems to mean the first approach is better. so gamma is multiplied by the number of shares of OI this gives the total amount of shares that are exposed/ being hedged but it does not give you the price, multiplying by the spot price tells us the dollar amount at the current price of the stock
 

sudoshu

Member
Here's another update per my question on the different formulas. I added an input to select the calculation method.

Code:
#hint: Options Hacker \n This study lets you scan the options chain for a series and given depth. \n<b>Warning: Setting the StrikeDepth to large values requires significant processing power, and will result in slow loading times.</b>


#-----------------------------------------------------------------------------------------------------------------#
# Settings

# Colors
DefineGlobalColor("Call", Color.GREEN);
DefineGlobalColor("Put", Color.RED);
DefineGlobalColor("CallCloud",Color.DARK_GREEN);
DefineGlobalColor("PutCloud",Color.DARK_RED);

#hint Mode: The mode to select an option symbol. \n AUTO will try to find the option symbol based on the Series and StrikeDepth inputs. \n MANUAL allow an override of the AUTO behavior by using the ManualCenterStrike and ManualStrikeSpacing inputs to determine the option symbol.
input Mode = {default AUTO, MANUAL};

#hint Series: The option expiration series to search. \n This value is used to determine the option symbol.
input Series = {
    default Weekly,
    Month1,
    Month2,
    Month3,
    Month4,
    Month5,
    Month6,
    Month7,
    Month8,
    Month9
};

#hint DataType: The type of option data to show.
input DataType = {default OpenInterest, Volume, GammaExposure};

#hint StrikeDepth: The level of depth to search a series. (+/- this far from ATM)
input StrikeDepth = 10;

#hint CenterStrikeOffset: The offset to use when calculating the center strike based on close price. \n Examples: \n   1 = nearest $1 interval \n   10 = nearest $10 interval.
input CenterStrikeOffset = 1.0;

#hint MaxStrikeSpacing: The maximum dollar amount between two adjacent contracts.
input MaxStrikeSpacing = 25;

#hint ManualCenterStrike: The starting price to use when in MANUAL mode.
input ManualCenterStrike = 440;

#hint ManualStrikeSpacing: The dollar amount between two adjacent contracts to use when in MANUAL mode.
input ManualStrikeSpacing = 1.0;

#hint ShowStrikeInfo: Show the strike info labels.
input ShowStrikeInfo = yes;

#hint ShowLabels: Show the open interest labels.
input ShowLabels = yes;

#hint ShowClouds: Show the open interest clouds.
input ShowClouds = no;

#hint ShowLines: Show the open interest lines.
input ShowLines = yes;

#hint ShowGreeks: Show the estimated Greek calculation labels for the latest bar.
input ShowGreeks = yes;


#-----------------------------------------------------------------------------------------------------------------#
# Date, Symbol, and Strike

# OptionSeries is the expiry starting at 1 and raising by one for each next expiry.
def OptionSeries;
switch (Series) {
    case Weekly:
        OptionSeries = 1;
    case Month1:
        OptionSeries = 2;
    case Month2:
        OptionSeries = 3;
    case Month3:
        OptionSeries = 4;
    case Month4:
        OptionSeries = 5;
    case Month5:
        OptionSeries = 6;
    case Month6:
        OptionSeries = 7;
    case Month7:
        OptionSeries = 8;
    case Month8:
        OptionSeries = 9;
    case Month9:
        OptionSeries = 10;
};

# Open price at Regular Trading Hours
def RTHopen = open(period = AggregationPeriod.DAY);

# Current year, month, day, and date
def CurrentYear = GetYear(); # number of current bar in CST
def CurrentMonth = GetMonth(); # 1 - 12
def CurrentDay = GetDay(); # 1 - 365 (366 for leap year)
def CurrentDate = GetYYYYMMDD(); # date of the current bar in the YYYYMMDD

# Current day of this month
def CurrentDayOfMonth = GetDayOfMonth(CurrentDate);

# Get the first day of this month - 1 (Monday) to 7 (Sunday)
def FirstDayThisMonth = GetDayOfWeek((CurrentYear * 10000) + (CurrentMonth * 100) + 1);

# Get the first upcoming friday
def FirstUpcomingFriday =
    if FirstDayThisMonth < 6 then 6 - FirstDayThisMonth
    else if FirstDayThisMonth == 6 then 7
    else 6
;

# Get the second, third, and fourth upcoming fridays
def SecondUpcomingFriday = FirstUpcomingFriday + 7;
def ThirdUpcomingFriday = FirstUpcomingFriday + 14;
def FourthUpcomingFriday = FirstUpcomingFriday + 21;

# Pick up all Fridays of the current month for weekly options
def RollDOM = FirstUpcomingFriday + 21;
def ExpMonth1 =
    if RollDOM > CurrentDayOfMonth then CurrentMonth + OptionSeries - 1
    else CurrentMonth + OptionSeries
;

# Options month input
def ExpMonth2 = if ExpMonth1 > 12 then ExpMonth1 - 12 else ExpMonth1;

# Options year input
def ExpYear = if ExpMonth1 > 12 then CurrentYear + 1 else CurrentYear;

# First friday expiry calc
def ExpDay1DOW = GetDayOfWeek(ExpYear * 10000 + ExpMonth2 * 100 + 1);
def ExpFirstFridayDOM =
    if ExpDay1DOW < 6 then 6 - ExpDay1DOW
    else if ExpDay1DOW == 6 then 7
    else 6
;

# Options code day of month input
def ExpDOM =
    if CurrentDayOfMonth < ExpFirstFridayDOM -1 then FirstUpcomingFriday
    else if between(CurrentDayOfMonth, ExpFirstFridayDOM, SecondUpcomingFriday - 1) then SecondUpcomingFriday
    else if between(CurrentDayOfMonth, SecondUpcomingFriday, ThirdUpcomingFriday - 1) then ThirdUpcomingFriday
    else if between(CurrentDayOfMonth, ThirdUpcomingFriday, FourthUpcomingFriday - 1) then FourthUpcomingFriday
    else ExpFirstFridayDOM
;

# Option Expiration Date - This is still some voodoo to me ...
def OptionExpiryDate = ExpYear * 10000 + ExpMonth2 * 100 + ExpDOM + 1;

# Option Days to Expiration
def DTE = AbsValue(CountTradingDays(CurrentDate, OptionExpiryDate) - 1);

# Centerstrike
def CenterStrike =
    if (Mode == Mode.AUTO and !IsNaN(close)) then Round(close / CenterStrikeOffset, 0) * CenterStrikeOffset
    else if (Mode == Mode.MANUAL and !IsNaN(close)) then ManualCenterStrike
    else CenterStrike[1]
;



# Strike Spacing
def StrikeSpacingC =
    fold i = 1 to MaxStrikeSpacing
    with spacing = 0
    do if DataType == DataType.OpenInterest or DataType == DataType.GammaExposure and !IsNaN(
        open_interest(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "P" + AsPrice(CenterStrike + (MaxStrikeSpacing - i)))
    )
    then MaxStrikeSpacing - i
    else if DataType == DataType.Volume and !IsNaN(
        volume(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "P" + AsPrice(CenterStrike + (MaxStrikeSpacing - i)))
    )
    then MaxStrikeSpacing - i
    else spacing
;
def StrikeSpacing =
    if (Mode == Mode.AUTO and !IsNaN(close)) then StrikeSpacingC
    else if (Mode == Mode.MANUAL and !IsNaN(close)) then ManualStrikeSpacing
    else StrikeSpacing[1]
;






#-----------------------------------------------------------------------------------------------------------------#
# Option Chain Data Gathering


# Total Put Open Interest for selected chain depth and expiry series
def TotalPutOpenInterest =
    fold poiIndex = -(StrikeDepth) to StrikeDepth
    with poi = 0
    do
        if DataType == DataType.OpenInterest and !IsNaN(
            open_interest(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "P" + AsPrice(CenterStrike + (StrikeSpacing * poiIndex)))
        )
        then poi + open_interest(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "P" + AsPrice(CenterStrike + (StrikeSpacing * poiIndex)))
        else 0
;


# Total Call Open Interest for selected chain depth and expiry series
def TotalCallOpenInterest =
    fold coiIndex = -(StrikeDepth) to StrikeDepth
    with coi = 0
    do
        if DataType == DataType.OpenInterest and !IsNaN(
            open_interest(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "C" + AsPrice(CenterStrike + (StrikeSpacing * coiIndex)))
        )
        then coi + open_interest(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "C" + AsPrice(CenterStrike + (StrikeSpacing * coiIndex)))
        else 0
;


# Total Put Volume for selected chain depth and expiry series
def TotalPutVolume =
    fold pvIndex = -(StrikeDepth) to StrikeDepth
    with pv = 0
    do
        if DataType == DataType.Volume and !IsNaN(
            volume(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "P" + AsPrice(CenterStrike + StrikeSpacing * pvIndex))
        )
        then pv + volume(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "P" + AsPrice(CenterStrike + StrikeSpacing * pvIndex))
        else 0
;


# Total Call Open Interest for selected chain depth and expiry series
def TotalCallVolume =
    fold cvIndex = -(StrikeDepth) to StrikeDepth
    with cv = 0
    do
        if DataType == DataType.Volume and !IsNaN(
            volume(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "C" + AsPrice(CenterStrike + StrikeSpacing * cvIndex))
        )
        then cv + volume(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "C" + AsPrice(CenterStrike + StrikeSpacing * cvIndex))
        else 0
;


#-----------------------------------------------------------------------------------------------------------------#
# Greeks Calculations
#
# K  - Option strike price
# N  - Standard normal cumulative distribution function
# r  - Risk free interest rate
# IV - Volatility of the underlying
# S  - Price of the underlying
# t  - Time to option's expiry
#
# d1    = (ln(S/K) + (r + (sqr(IV)/2))t) / (? (sqrt(t)))
# d2    = e -(sqr(d1) / 2) / sqrt(2*pi)
#
# Delta = N(d1)
# Gamma = (d2) / S(IV(sqrt(t)))
# Theta = ((S d2))IV) / 2 sqrt(t)) - (rK e(rt)N(d4))
#         where phi(d3) = (exp(-(sqr(x)/2))) / (2 * sqrt(t))
#         where d4 = d1 - IV(sqrt(t))
# Vega  = S phi(d1) Sqrt(t)


# Get the implied volatility for calculations
# Input: series is the expiry starting at 1 and raising by 1 for each next expiry
def IV = SeriesVolatility(series = OptionSeries);

def K = CenterStrike;
def S = close;

def r = GetInterestRate();
def t = (DTE / 365);
def d1 = (Log(S / K) + ((r + (Sqr(IV) / 2)) * t)) / (IV * Sqrt(t));
def d2 = Exp(-(Sqr(d1) / 2)) / Sqrt(2 * Double.Pi);
script N {
    input data  = 1;
    def a = AbsValue(data);
    def b1 =  .31938153;
    def b2 = -.356563782;
    def b3 = 1.781477937;
    def b4 = -1.821255978;
    def b5 = 1.330274429;
    def b6 =  .2316419;
    def e = 1 / (1 + b6 * a);
    def i = 1 - 1 / Sqrt(2 * Double.Pi) * Exp(-Power(a, 2) / 2) *
           (b1 * e + b2 * e * e + b3 *
            Power(e, 3) + b4 * Power(e, 4) + b5 * Power(e, 5));
    plot CND = if data < 0
               then 1 - i
               else i;
}


# TODO: These values don't quite line up
# My background on options pricing models is not very good

# Delta
def Delta = N(d1);

# Gamma
def Gamma = d2 / (S * (IV * Sqrt(t)));

# Theta
def Theta = -(-(S*d2*IV*(.5000)/
             (2*sqrt(t)))-
             (r*(exp(-r*t)*K))*N(d2)+(S*N(d1)*(.5000)))/365;
# (.5000) variant less than .5 e(X/t)

# Vega
def Vega = (S*d2*sqrt(t))/100;


script SpotGamma {
    input DTE = 0;
    input K = 0;
    input Series = 0;
    def S = close;
    def t = (DTE / 365);
    def IV = SeriesVolatility(series = Series);
    def d1 = (Log(S / K) + ((GetInterestRate() + (Sqr(IV) / 2)) * t)) / (IV * Sqrt(t));

    # Delta
    def Delta = N(d1);

    # Gamma
    def d2 = Exp(-(Sqr(d1) / 2)) / Sqrt(2 * Double.Pi);
    def Gamma = d2 / (S * (IV * Sqrt(t)));
    
    plot SpotGamma = Gamma;
}

input GEXCalculationMethod = {default ContributionShares, Contribution, ContributionPercent};
def GEXMethod;
switch (GEXCalculationMethod) {
    case Contribution:
        GEXMethod = 1;
    case ContributionPercent:
        GEXMethod = 2;
    case ContributionShares:
        GEXMethod = 3;
}

# Total Put Gamma Exposure for selected chain depth and expiry series
def TotalPutGammaExposure =
    fold pgexIndex = -(StrikeDepth) to StrikeDepth
    with pgex = 0
    do
        if DataType == DataType.GammaExposure and !IsNaN(
            open_interest(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "P" + AsPrice(CenterStrike + (StrikeSpacing * pgexIndex)))
        )
        then pgex +
            open_interest(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "P" + AsPrice(CenterStrike + (StrikeSpacing * pgexIndex))) *
            if GEXMethod == 1 then
                OptionPrice((CenterStrike + (StrikeSpacing * pgexIndex)), yes, DTE, close, IV, no, 0.0, r)
            else if GEXMethod == 2 then
                Sqr(OptionPrice((CenterStrike + (StrikeSpacing * pgexIndex)), yes, DTE, close, IV, no, 0.0, r)) * 0.01
            else
                1
            #SpotGamma(DTE, (CenterStrike + (StrikeSpacing * pgexIndex)), OptionSeries) * # Honestly why cant I put the index inside here :(
            * (Exp(-(Sqr((Log(close / (CenterStrike + (StrikeSpacing * pgexIndex))) + ((r + (Sqr(IV) / 2)) * t)) / (IV * Sqrt(t))) / 2)) / Sqrt(2 * Double.Pi) / (close * (IV * Sqrt(t)))) *
            100 *
            -1
        else 0
;

# Total Call Gamma Exposure for selected chain depth and expiry series
def TotalCallGammaExposure =
    fold cgexIndex = -(StrikeDepth) to StrikeDepth
    with cgex = 0
    do
        if DataType == DataType.GammaExposure and !IsNaN(
            open_interest(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "C" + AsPrice(CenterStrike + (StrikeSpacing * cgexIndex)))
        )
        then cgex +
            open_interest(("." + GetSymbolPart()) + AsPrice(OptionExpiryDate - 20000001) + "C" + AsPrice(CenterStrike + (StrikeSpacing * cgexIndex))) *
            if GEXMethod == 1 then
                OptionPrice((CenterStrike + (StrikeSpacing * cgexIndex)), no, DTE, close, IV, no, 0.0, r)
            else if GEXMethod == 2 then
                Sqr(OptionPrice((CenterStrike + (StrikeSpacing * cgexIndex)), no, DTE, close, IV, no, 0.0, r)) * 0.01
            else
                1
            #SpotGamma(DTE, (CenterStrike + (StrikeSpacing * cgexIndex)), OptionSeries) * # Honestly why cant I put the index inside here :(
            * (Exp(-(Sqr((Log(close / (CenterStrike + (StrikeSpacing * cgexIndex))) + ((r + (Sqr(IV) / 2)) * t)) / (IV * Sqrt(t))) / 2)) / Sqrt(2 * Double.Pi) / (close * (IV * Sqrt(t)))) *
            100
        else 0
;

plot GammaExposure = TotalPutGammaExposure + TotalCallGammaExposure;


#-----------------------------------------------------------------------------------------------------------------#
# Visuals

# Selected DataType
AddLabel(yes, DataType, Color.LIGHT_GRAY);

# Selected Series
AddLabel(yes, Series, Color.LIGHT_GRAY);

# Center Strike Label
AddLabel(ShowStrikeInfo, "Center Strike: " + AsDollars(CenterStrike), Color.LIGHT_GRAY);

# Chain Depth Label
AddLabel(ShowStrikeInfo, "Strike Depth: +/-" + StrikeDepth, Color.LIGHT_GRAY);

# Strike Spacing Label
AddLabel(ShowStrikeInfo, "Strike Spacing: " + AsDollars(StrikeSpacing), Color.LIGHT_GRAY);

# Current ATM Options Labels
Addlabel(ShowStrikeInfo, "ATM Put: " + ("." + GetSymbol()) + AsPrice(OptionExpiryDate - 20000001) + "P" + AsPrice(CenterStrike), Color.LIGHT_RED);
Addlabel(ShowStrikeInfo, "ATM Call: " + ("." + GetSymbol()) + AsPrice(OptionExpiryDate - 20000001) + "C" + AsPrice(CenterStrike), Color.LIGHT_GREEN);

# Create a center line
plot ZeroLine = 0;
ZeroLine.SetDefaultColor(Color.WHITE);

# Call Open Interest
plot CallOpenInterest = TotalCallOpenInterest;
CallOpenInterest.SetHiding(!ShowLines and DataType == DataType.OpenInterest);
CallOpenInterest.SetPaintingStrategy(PaintingStrategy.LINE);
CallOpenInterest.SetDefaultColor(GlobalColor("Call"));
AddLabel(ShowLabels and DataType == DataType.OpenInterest, "CallOI: " + CallOpenInterest, GlobalColor("Call"));

# Put Open Interest
plot PutOpenInterest = -(TotalPutOpenInterest); # Make negative to flip under axis
PutOpenInterest.SetHiding(!ShowLines and DataType == DataType.OpenInterest);
PutOpenInterest.SetPaintingStrategy(PaintingStrategy.LINE);
PutOpenInterest.SetDefaultColor(GlobalColor("Put"));
AddLabel(ShowLabels and DataType == DataType.OpenInterest, "PutOI: " + TotalPutOpenInterest, GlobalColor("Put"));

# Create Clouds for Open Interest
AddCloud(
    if ShowClouds and DataType == DataType.OpenInterest then CallOpenInterest else Double.NaN,
    if ShowClouds and DataType == DataType.OpenInterest then Zeroline else Double.NaN,
    GlobalColor("CallCloud"), GlobalColor("PutCloud")
);
AddCloud(
    if ShowClouds and DataType == DataType.OpenInterest then Zeroline else Double.NaN,
    if ShowClouds and DataType == DataType.OpenInterest then PutOpenInterest else Double.NaN,
    GlobalColor("PutCloud"), GlobalColor("CallCloud")
);

# Hull Moving Average of Put Open Interest
plot PutOpenInterestAverage = hullmovingavg(PutOpenInterest);
PutOpenInterestAverage.SetHiding(!ShowLines and DataType == DataType.OpenInterest);
PutOpenInterestAverage.SetDefaultColor(Color.ORANGE);
PutOpenInterestAverage.SetStyle(Curve.MEDIUM_DASH);

# Hull Moving Average of Call Open Interest
plot CallOpenInterestAverage = hullmovingavg(CallOpenInterest);
CallOpenInterestAverage.SetHiding(!ShowLines and DataType == DataType.OpenInterest);
CallOpenInterestAverage.SetDefaultColor(Color.LIGHT_GREEN);
CallOpenInterestAverage.SetStyle(Curve.MEDIUM_DASH);

# Color Gradient of Total Average Open Interest
#plot TotalOpenInterestAverage = average(CallOpenInterest + PutOpenInterest);
#TotalOpenInterestAverage.SetHiding(!ShowLines and DataType == DataType.OpenInterest);
#TotalOpenInterestAverage.SetLineWeight(3);
#TotalOpenInterestAverage.DefineColor("Default", Color.YELLOW);
#TotalOpenInterestAverage.DefineColor("Highest", Color.GREEN);
#TotalOpenInterestAverage.DefineColor("Lowest", Color.RED);
#TotalOpenInterestAverage.AssignNormGradientColor(14, TotalOpenInterestAverage.Color("Lowest"), TotalOpenInterestAverage.Color("Highest"));
#AddCloud(
#    if ShowClouds and DataType == DataType.OpenInterest then TotalOpenInterestAverage else Double.NaN,
#    if ShowClouds and DataType == DataType.OpenInterest then Zeroline else Double.NaN,
#    color.GREEN, color.RED
#);

# Call Volume
plot CallVolume = TotalCallVolume;
CallVolume.SetHiding(!ShowLines and DataType == DataType.Volume);
CallVolume.SetPaintingStrategy(PaintingStrategy.LINE);
CallVolume.SetDefaultColor(GlobalColor("Call"));
AddLabel(ShowLabels and DataType == DataType.Volume, "CallVol: " + CallVolume, GlobalColor("Call"));

# Put Volume
plot PutVolume = -(TotalPutVolume); # Make negative to flip under axis
PutVolume.SetHiding(!ShowLines and DataType == DataType.Volume);
PutVolume.SetPaintingStrategy(PaintingStrategy.LINE);
PutVolume.SetDefaultColor(GlobalColor("Put"));
AddLabel(ShowLabels and DataType == DataType.Volume, "PutVol: " + TotalPutVolume, GlobalColor("Put"));

# Create Clouds for Volume
AddCloud(
    if ShowClouds and DataType == DataType.Volume then CallVolume else Double.NaN,
    if ShowClouds and DataType == DataType.Volume then Zeroline else Double.NaN,
    GlobalColor("CallCloud"), GlobalColor("PutCloud")
);
AddCloud(
    if ShowClouds and DataType == DataType.Volume then Zeroline else Double.NaN,
    if ShowClouds and DataType == DataType.Volume then PutVolume else Double.NaN,
    GlobalColor("PutCloud"), GlobalColor("CallCloud")
);

# Hull Moving Average of Put Volume
plot PutVolumeAverage = hullmovingavg(PutVolume);
PutVolumeAverage.SetHiding(!ShowLines and DataType == DataType.Volume);
PutVolumeAverage.SetDefaultColor(Color.ORANGE);
PutVolumeAverage.SetStyle(Curve.MEDIUM_DASH);

# Hull Moving Average of Call Volume
plot CallVolumeAverage = hullmovingavg(CallVolume);
CallVolumeAverage.SetHiding(!ShowLines and DataType == DataType.Volume);
CallVolumeAverage.SetDefaultColor(Color.LIGHT_GREEN);
CallVolumeAverage.SetStyle(Curve.MEDIUM_DASH);

# Color Gradient of Total Average Volume
#plot TotalVolumeAverage = average(CallVolume + PutVolume);
#TotalVolumeAverage.SetHiding(!ShowLines and DataType == DataType.Volume);
#TotalVolumeAverage.SetLineWeight(3);
#TotalVolumeAverage.DefineColor("Default", Color.YELLOW);
#TotalVolumeAverage.DefineColor("Highest", Color.GREEN);
#TotalVolumeAverage.DefineColor("Lowest", Color.RED);
#TotalVolumeAverage.AssignNormGradientColor(14, TotalVolumeAverage.Color("Lowest"), TotalVolumeAverage.Color("Highest"));
#AddCloud(
#    if ShowClouds and DataType == DataType.Volume then TotalVolumeAverage else Double.NaN,
#    if ShowClouds and DataType == DataType.Volume then Zeroline else Double.NaN,
#    Color.GREEN, Color.RED
#);

# Greeks Labels
AddLabel(ShowGreeks, "Delta: " + Delta, Color.WHITE);
AddLabel(ShowGreeks, "Gamma: " + Gamma, Color.WHITE);
AddLabel(ShowGreeks, "Theta: " + Theta, Color.WHITE);
AddLabel(ShowGreeks, "Vega: " + Vega, Color.WHITE);
 

sudoshu

Member
using I7-32GB, all the other lines come up right away but in the gamma exposure line nothing happens
Hmm, I do think there might still be a weird bug with the auto strike spacing selection, haven't fully figured out how that code is working yet.
What symbol are you trying this on and with what settings? I'll try and replicate if possible.
 

ziongotoptions

Active member
Weird, it shows up just fine for me. Actually loaded super fast (does the chart data come along with the shared workspace so that I didn't have to load it again? if so thats very nice)

Edit: Could it be the color of the line somehow?

HBGdsoJ.png
How did you remove the other lines? Did you go into settings and change anything or did it pop up just like that?
 

sudoshu

Member
How did you remove the other lines? Did you go into settings and change anything or did it pop up just like that?
The lines should appear and disappear automatically based on the 'data type' setting.
I just went in and set it to 'GammaExposure'

Edit: I didnt hook up the ShowLines part to the GEX plot yet so as long as you select the datatype for GEX it should show

cHaRI5W.png
 

ziongotoptions

Active member
The lines should appear and disappear automatically based on the 'data type' setting.
I just went in and set it to 'GammaExposure'

Edit: I didnt hook up the ShowLines part to the GEX plot yet so as long as you select the datatype for GEX it should show

cHaRI5W.png
That was what I needed, thank you!! Btw for zero gamma we need to use spot price , however you just want to find high gamma levels we can just use GEX, the total gamma contribution just gives an approximate value of how much dealers are positioned on one side or another of the market
 

sudoshu

Member
That was what I needed, thank you!! Btw for zero gamma we need to use spot price , however you just want to find high gamma levels we can just use GEX, the total gamma contribution just gives an approximate value of how much dealers are positioned on one side or another of the market
Ahhh I think I understand now. I've added a switch and a version number to the script now to make it easier to track if someone has an issue.
Should I make a new thread for this under the indicators section? I don't want to step on any toes as a lot of work was done by others before me in this thread and others I've found here. I plan on adding a credits section in the code to acknowledge everyone anyways.

I realize this specific thread was about the heatmap, which would be cool to get working with this as well. Thoughts?
 

ziongotoptions

Active member
The lines should appear and disappear automatically based on the 'data type' setting.
I just went in and set it to 'GammaExposure'

Edit: I didnt hook up the ShowLines part to the GEX plot yet so as long as you select the datatype for GEX it should show

cHaRI5W.png
Btw I think this is working well if you look at the trend on spy , before when we were in an uptrend , gamma line rarely fell below 0 now we are in a downtrend and gamma is usually below 0. I’m using 20 strikes out
 

ziongotoptions

Active member
Ahhh I think I understand now. I've added a switch and a version number to the script now to make it easier to track if someone has an issue.
Should I make a new thread for this under the indicators section? I don't want to step on any toes as a lot of work was done by others before me in this thread and others I've found here. I plan on adding a credits section in the code to acknowledge everyone anyways.

I realize this specific thread was about the heatmap, which would be cool to get working with this as well. Thoughts?
I definitely think you should make you’re own thread , you’ve done incredible work.
 

ziongotoptions

Active member
Ahhh I think I understand now. I've added a switch and a version number to the script now to make it easier to track if someone has an issue.
Should I make a new thread for this under the indicators section? I don't want to step on any toes as a lot of work was done by others before me in this thread and others I've found here. I plan on adding a credits section in the code to acknowledge everyone anyways.

I realize this specific thread was about the heatmap, which would be cool to get working with this as well. Thoughts?
Also check out the volume open interest profile we made, I would be very interested in a option gamma profile aswell
 

sudoshu

Member
I definitely think you should make you’re own thread , you’ve done incredible work.
Will do. I need a bit to get it all straightened out but I will update this post with a link when its ready for future readers.

Also check out the volume open interest profile we made, I would be very interested in a option gamma profile aswell
Is this the upper chart version that is plotted to the right? I agree making that into a gamma profile version would be sweet.
 

ziongotoptions

Active member
Will do. I need a bit to get it all straightened out but I will update this post with a link when its ready for future readers.


Is this the upper chart version that is plotted to the right? I agree making that into a gamma profile version would be sweet.
Yes! I currently assume high gamma level is where the highest open interest is, would be cool to see if that’s the case. Take time reading the script, building that was quite unconventional, we can’t take all the credit , but we reverse engineered it and tried to improve it
 

TMac

New member
I`m trying to do something similar to this code. I would like to plot a simple horizontal line at a specified strike price that meets my criteria.
More specifically, I want to plot lines at strikes that are closest to predefined delta values. I would greatly appreciate any help.
 

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