Asay (1982) Margined Futures Option Pricing Model [Loxx]
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © loxx

//@version=5
indicator("Asay (1982) Margined Futures Option Pricing Model [Loxx]", 
     shorttitle ="A1982MFOPM [Loxx]",
     overlay = true, 
     max_lines_count = 500, precision = 4)

if not timeframe.isdaily
    runtime.error("Error: Invald timeframe. Indicator only works on daily timeframe.") 

import loxx/loxxexpandedsourcetypes/4
import loxx/cnd/1

color darkGreenColor = #1B7E02 
string callString = "Call"
string putString = "Put"

string Continuous = "Continuous"
string PeriodRate = "Period Rate"
string Annual = "Annual"
string SemiAnnual = "Semi-Annual"
string Quaterly = "Quaterly"
string Monthly = "Monthly"

string rogersatch = "Roger-Satchell"
string parkinson = "Parkinson"
string c2c = "Close-to-Close"
string gkvol = "Garman-Klass"
string gkzhvol = "Garman-Klass-Yang-Zhang"
string ewmavolstr = "Exponential Weighted Moving Average"

string timtoolbar= "Time Now = Current time in UNIX format. It is the number of milliseconds that have elapsed since 00:00:00 UTC, 1 January 1970."
string timtoolnow = "Time Bar = The time function returns the UNIX time of the current bar for the specified timeframe and session or NaN if the time point is out of session."
string timetooltrade = "Trading Day = The beginning time of the trading day the current bar belongs to, in UNIX format (the number of milliseconds that have elapsed since 00:00:00 UTC, 1 January 1970)."

ewmavol(float src, int per) => 
    float lambda = (per - 1) / (per + 1)
    float temp = na
    temp := lambda * nz(temp[1], math.pow(src, 2)) + (1.0 - lambda) * math.pow(src, 2)
    out = math.sqrt(temp)
    out
    
rogerssatchel(int per) =>
    float sum = math.sum(math.log(high/ close) * math.log(high / open)
         + math.log(low / close) * math.log(low / open), per) / per
    float out = math.sqrt(sum) 
    out

closetoclose(float src, int per) => 
    float avg = ta.sma(src, per)
    array<float> sarr = array.new<float>(per, 0)
    for i = 0 to per - 1 
        array.set(sarr, i,  math.pow(nz(src[i]) - avg, 2))
    float out = math.sqrt(array.sum(sarr) / (per - 1))
    out 

parkinsonvol(int per)=>
    float volConst = 1.0 / (4.0 * per * math.log(2))
    float sum = volConst * math.sum(math.pow(math.log(high / low), 2), per)
    float out = math.sqrt(sum) 
    out

garmanKlass(int per)=>
    float hllog = math.log(high / low)
    float oplog = math.log(close / open)
    float garmult = (2 * math.log(2) - 1)
    float parkinsonsum = 1 / (2 * per) * math.sum(math.pow(hllog, 2), per)
    float garmansum = garmult / per * math.sum(math.pow(oplog, 2), per)
    float sum = parkinsonsum - garmansum
    float devpercent = math.sqrt(sum)  
    devpercent

gkyzvol(int per)=>
    float gzkylog = math.log(open / nz(close[1]))
    float pklog = math.log(high / low)
    float gklog = math.log(close / open)
    float garmult = (2 * math.log(2) - 1)
    float gkyzsum = 1 / per * math.sum(math.pow(gzkylog, 2), per)
    float parkinsonsum = 1 / (2 * per) * math.sum(math.pow(pklog, 2), per)
    float garmansum = garmult / per * math.sum(math.pow(gklog, 2), per)
    float sum = gkyzsum + parkinsonsum - garmansum
    float devpercent = math.sqrt(sum)  
    devpercent

nd(float x)=> 
    float out = math.exp(-x * x * 0.5) / math.sqrt(2 * math.pi)
    out 

convertingToCCRate(float r, float Compoundings)=>
    float ConvertingToCCRate = 0
    if Compoundings == 0 
        ConvertingToCCRate := r
    else
        ConvertingToCCRate := Compoundings * math.log(1 + r / Compoundings)
    ConvertingToCCRate

// DDeltaDvol also known as vanna
GDdeltaDvol(float S, float x, float T, float r, float b, float v)=>
    float d1 = 0
    float d2 = 0
    float GDdeltaDvol = 0
    d1 := (math.log(S / x) + (b + v * v / 2) * T) / (v * math.sqrt(T))
    d2 := d1 - v * math.sqrt(T)
    GDdeltaDvol := -math.exp((b - r) * T) * d2 / v * nd(d1)
    GDdeltaDvol

GBlackScholes(string CallPutFlag, float S, float x, float T, float r, float b, float v)=>
    float d1 = 0
    float d2 = 0
    float gBlackScholes = 0

    d1 := (math.log(S / x) + (b + math.pow(v, 2) / 2) * T) / (v * math.sqrt(T))
    d2 := d1 - v * math.sqrt(T)

    if CallPutFlag == callString
        gBlackScholes := S * math.exp((b - r) * T) * cnd.CND1(d1) - x * math.exp(-r * T) * cnd.CND1(d2)
    else
        gBlackScholes := x * math.exp(-r * T) * cnd.CND1(-d2) - S * math.exp((b - r) * T) * cnd.CND1(-d1)
    gBlackScholes

// Gamma for the generalized Black and Scholes formula
GGamma(float S, float x, float T, float r, float b, float v)=>
    float d1 = 0
    d1 := (math.log(S / x) + (b + math.pow(v, 2) / 2) * T) / (v * math.sqrt(T))
    GGamma = math.exp((b - r) * T) * nd(d1) / (S * v * math.sqrt(T))
    GGamma

// GammaP for the generalized Black and Scholes formula
GGammaP(float S, float x, float T, float r, float b, float v)=>
    GGammaP = S * GGamma(S, x, T, r, b, v) / 100
    GGammaP

// Delta for the generalized Black and Scholes formula
GDelta(string CallPutFlag, float S, float x, float T, float r, float b, float v)=>
    float d1 = 0
    float GDelta = 0
    d1 := (math.log(S / x) + (b + math.pow(v, 2) / 2) * T) / (v * math.sqrt(T))
    if CallPutFlag == callString 
        GDelta := math.exp((b - r) * T) * cnd.CND1(d1)
    else
        GDelta := -math.exp((b - r) * T) * cnd.CND1(-d1)
    GDelta

// StrikeDelta for the generalized Black and Scholes formula
GStrikeDelta(string CallPutFlag, float S, float x, float T, float r, float b, float v)=>
    float d2 = 0
    float GStrikeDelta = 0
    d2 := (math.log(S / x) + (b - math.pow(v, 2) / 2) * T) / (v * math.sqrt(T))
    if CallPutFlag == callString 
        GStrikeDelta := -math.exp(-r * T) * cnd.CND1(d2)
    else
        GStrikeDelta := math.exp(-r * T) * cnd.CND1(-d2)
    GStrikeDelta

// Elasticity for the generalized Black and Scholes formula
GElasticity(string CallPutFlag, float S, float x, float T, float r, float b, float v)=>
    GElasticity = GDelta(CallPutFlag, S, x, T, r, b, v) * S / GBlackScholes(CallPutFlag, S, x, T, r, b, v)
    GElasticity

// Risk Neutral Denisty for the generalized Black and Scholes formula
GRiskNeutralDensity(float S, float x, float T, float r, float b, float v)=>
    float d2  = 0
    d2 := (math.log(S / x) + (b - math.pow(v, 2) / 2) * T) / (v * math.sqrt(T))
    GRiskNeutralDensity = math.exp(-r * T) * nd(d2) / (x * v * math.sqrt(T))
    GRiskNeutralDensity

// Theta for the generalized Black and Scholes formula
GTheta(string CallPutFlag, float S, float x, float T, float r, float b, float v)=>
    float d1 = 0
    float d2 = 0
    float GTheta = 0
    d1 := (math.log(S / x) + (b + math.pow(v, 2) / 2) * T) / (v * math.sqrt(T))
    d2 := d1 - v * math.sqrt(T)
    if CallPutFlag == callString
        GTheta := -S * math.exp((b - r) * T) * nd(d1) * v / (2 * math.sqrt(T)) - (b - r) * S * math.exp((b - r) * T) * cnd.CND1(d1) - r * x * math.exp(-r * T) * cnd.CND1(d2)
    else 
        GTheta := -S * math.exp((b - r) * T) * nd(d1) * v / (2 * math.sqrt(T)) + (b - r) * S * math.exp((b - r) * T) * cnd.CND1(-d1) + r * x * math.exp(-r * T) * cnd.CND1(-d2)
    GTheta

// Vega for the generalized Black and Scholes formula
GVega(float S, float x, float T, float r, float b, float v)=>
    float d1 = 0
    d1 := (math.log(S / x) + (b + math.pow(v, 2) / 2) * T) / (v * math.sqrt(T))
    GVega = S * math.exp((b - r) * T) * nd(d1) * math.sqrt(T)
    GVega

// VegaP for the generalized Black and Scholes formula
GVegaP(float S, float x, float T, float r, float b, float v)=>
    GVegaP = v / 10 * GVega(S, x, T, r, b, v)
    GVegaP

// DvegaDvol/Vomma for the generalized Black and Scholes formula
GDvegaDvol(float S, float x, float T, float r, float b, float v)=>
    float d1 = 0
    float d2 = 0    
    d1 := (math.log(S / x) + (b + math.pow(v, 2) / 2) * T) / (v * math.sqrt(T))
    d2 := d1 - v * math.sqrt(T)
    GDvegaDvol = GVega(S, x, T, r, b, v) * d1 * d2 / v
    GDvegaDvol

// Rho for the generalized Black and Scholes formula for all options except futures

GRho(string CallPutFlag, float S, float x, float T, float r, float b, float v)=>
    float d1 = (math.log(S / x) + (b + v *v / 2) * T) / (v * math.sqrt(T))
    float d2 = d1 - v *  math.sqrt(T)
    float GRho = 0
    if CallPutFlag == callString
        GRho := T * x * math.exp(-r * T) * cnd.CND1(d2)
    else 
        GRho := -T * x * math.exp(-r * T) * cnd.CND1(-d2)
    GRho

// Rho for the generalized Black and Scholes formula for Futures option
GRhoFO(string CallPutFlag, float S, float x, float T, float r, float b, float v)=>
    GRhoFO = -T * GBlackScholes(CallPutFlag, S, x, T, r, 0, v)
    GRhoFO

// Rho2/Phi for the generalized Black and Scholes formula
GPhi(string CallPutFlag, float S, float x, float T, float r, float b, float v)=>
    float d1 = 0
    float GPhi = 0
    d1 := (math.log(S / x) + (b + math.pow(v, 2) / 2) * T) / (v * math.sqrt(T))
    if CallPutFlag == callString 
        GPhi := -T * S * math.exp((b - r) * T) * cnd.CND1(d1)
    else 
        GPhi := T * S * math.exp((b - r) * T) * cnd.CND1(-d1)
    GPhi

// Carry rf sensitivity for the generalized Black and Scholes formula
GCarry(string CallPutFlag, float S, float x, float T, float r, float b, float v)=>
    float d1 = 0
    float GCarry = 0
    d1 := (math.log(S / x) + (b + math.pow(v, 2) / 2) * T) / (v * math.sqrt(T))
    if CallPutFlag == callString
        GCarry := T * S * math.exp((b - r) * T) * cnd.CND1(d1)
    else 
        GCarry := -T * S * math.exp((b - r) * T) * cnd.CND1(-d1)
    GCarry

// DgammaDspot/Speed for the generalized Black and Scholes formula
GDgammaDspot(float S, float x, float T, float r, float b, float v)=>
    float d1 = 0
    float GDgammaDspot = 0
    d1 := (math.log(S / x) + (b + math.pow(v, 2) / 2) * T) / (v * math.sqrt(T))
    GDgammaDspot := -GGamma(S, x, T, r, b, v) * (1 + d1 / (v * math.sqrt(T))) / S
    GDgammaDspot

// DgammaDvol/Zomma for the generalized Black and Scholes formula
GDgammaDvol(float S, float x, float T, float r, float b, float v)=>
    float d1 = 0
    float d2 = 0 
    float GDgammaDvol = 0
    d1 := (math.log(S / x) + (b + math.pow(v, 2) / 2) * T) / (v * math.sqrt(T))
    d2 := d1 - v * math.sqrt(T)
    GDgammaDvol := GGamma(S, x, T, r, b, v) * ((d1 * d2 - 1) / v)
    GDgammaDvol 


CGBlackScholes(string OutPutFlag, string CallPutFlag, float S, float x, float T, float r, float b, float v)=>
    float output = 0
    float CGBlackScholes = 0

    if OutPutFlag == "p" // Value
        CGBlackScholes := GBlackScholes(CallPutFlag, S, x, T, r, b, v)
        
    //DELTA GREEKS
    else if OutPutFlag == "d" // Delta
        CGBlackScholes := GDelta(CallPutFlag, S, x, T, r, b, v)
    else if OutPutFlag == "dddv" // DDeltaDvol
        CGBlackScholes := GDdeltaDvol(S, x, T, r, b, v) / 100
    else if OutPutFlag == "e" //  Elasticity
        CGBlackScholes := GElasticity(CallPutFlag, S, x, T, r, b, v)

    //GAMMA GREEKS
    else if OutPutFlag == "g" // Gamma
        CGBlackScholes := GGamma(S, x, T, r, b, v)
    else if OutPutFlag == "gp" // GammaP
        CGBlackScholes := GGammaP(S, x, T, r, b, v)
    else if OutPutFlag == "s" // 'DgammaDspot/speed
        CGBlackScholes := GDgammaDspot(S, x, T, r, b, v)
    else if OutPutFlag == "gv" // 'DgammaDvol/Zomma
        CGBlackScholes := GDgammaDvol(S, x, T, r, b, v) / 100

    //VEGA GREEKS
    else if OutPutFlag == "v" // Vega
        CGBlackScholes := GVega(S, x, T, r, b, v) / 100
    else if OutPutFlag == "dvdv" // DvegaDvol/Vomma
        CGBlackScholes := GDvegaDvol(S, x, T, r, b, v) / 10000
    else if OutPutFlag == "vp" // VegaP
        CGBlackScholes := GVegaP(S, x, T, r, b, v)

    //THETA GREEKS
    else if OutPutFlag == "t" // Theta
        CGBlackScholes := GTheta(CallPutFlag, S, x, T, r, b, v) / 365

    //RATE/CARRY GREEKS
    else if OutPutFlag == "r" // Rho
        CGBlackScholes := GRho(CallPutFlag, S, x, T, r, b, v) / 100
    else if OutPutFlag == "f" // Phi/Rho2
        CGBlackScholes := GPhi(CallPutFlag, S, x, T, r, b, v) / 100
    else if OutPutFlag == "fr" // Rho futures option
        CGBlackScholes := GRhoFO(CallPutFlag, S, x, T, r, b, v) / 100

    //'PROB GREEKS
    else if OutPutFlag == "dx" // 'StrikeDelta
        CGBlackScholes := GStrikeDelta(CallPutFlag, S, x, T, r, b, v)
    else if OutPutFlag == "dxdx" // 'Risk Neutral Density
        CGBlackScholes := GRiskNeutralDensity(S, x, T, r, b, v)

    CGBlackScholes

gBlackScholesImpVolBisection(string CallPutFlag, float S, float x, float T, float r, float b, float cm)=>
    float vLow = 0
    float vHigh= 0
    float vi = 0
    float cLow = 0
    float cHigh = 0
    float epsilon = 0
    int counter = 0
    float gBlackScholesImpVolBisection = 0

    vLow := 0.005
    vHigh := 4
    epsilon := 1E-08
    cLow := GBlackScholes(CallPutFlag, S, x, T, r, b, vLow)
    cHigh := GBlackScholes(CallPutFlag, S, x, T, r, b, vHigh)

    vi := vLow + (cm - cLow) * (vHigh - vLow) / (cHigh - cLow)
    while math.abs(cm - GBlackScholes(CallPutFlag, S, x, T, r, b, vi)) > epsilon
        counter += 1 
        if counter == 100 
            gBlackScholesImpVolBisection := 0
            break
        if GBlackScholes(CallPutFlag, S, x, T, r, b, vi) < cm
            vLow := vi
        else
            vHigh := vi
        cLow := GBlackScholes(CallPutFlag, S, x, T, r, b, vLow)
        cHigh := GBlackScholes(CallPutFlag, S, x, T, r, b, vHigh)
        vi := vLow + (cm - cLow) * (vHigh - vLow) / (cHigh - cLow)

    gBlackScholesImpVolBisection := vi
    gBlackScholesImpVolBisection


gVega(float S, float x, float T, float r, float b, float v)=>
    float d1 = 0
    float GVega = 0
    d1 := (math.log(S / x) + (b + math.pow(v, 2) / 2) * T) / (v * math.sqrt(T))
    GVega := S * math.exp((b - r) * T) * nd(d1) * math.sqrt(T)
    GVega

gImpliedVolatilityNR(string CallPutFlag, float S, float x, float T, float r, float b, float cm, float epsilon)=>
    float vi = 0
    float ci = 0
    float vegai = 0
    float minDiff = 0
    float GImpliedVolatilityNR = 0

    vi := math.sqrt(math.abs(math.log(S / x) + r * T) * 2 / T)
    ci := GBlackScholes(CallPutFlag, S, x, T, r, b, vi)
    vegai := gVega(S, x, T, r, b, vi)
    minDiff := math.abs(cm - ci)

    while math.abs(cm - ci) >= epsilon and math.abs(cm - ci) <= minDiff
        vi := vi - (ci - cm) / vegai
        ci := GBlackScholes(CallPutFlag, S, x, T, r, b, vi)
        vegai := gVega(S, x, T, r, b, vi)
        minDiff := math.abs(cm - ci)
    if math.abs(cm - ci) < epsilon  
        GImpliedVolatilityNR := vi 
    else 
        GImpliedVolatilityNR := 0
    GImpliedVolatilityNR    

smthtype = input.string("Kaufman", "Heikin-Ashi Better Caculation Type", options = ["AMA", "T3", "Kaufman"], group = "Spot Price Settings")
srcin = input.string("Close", "Spot Price", group= "Spot Price Settings", 
     options = 
     ["Close", "Open", "High", "Low", "Median", "Typical", "Weighted", "Average", "Average Median Body", "Trend Biased", "Trend Biased (Extreme)", 
     "HA Close", "HA Open", "HA High", "HA Low", "HA Median", "HA Typical", "HA Weighted", "HA Average", "HA Average Median Body", "HA Trend Biased", "HA Trend Biased (Extreme)",
     "HAB Close", "HAB Open", "HAB High", "HAB Low", "HAB Median", "HAB Typical", "HAB Weighted", "HAB Average", "HAB Average Median Body", "HAB Trend Biased", "HAB Trend Biased (Extreme)"])

float K = input.float(275, "Strike Price", group = "Basic Settings")   
string OpType = input.string(callString, "Option type", options = [callString, putString], group = "Basic Settings")   
string side = input.string("Long", "Side", options = ["Long", "Short"], group = "Basic Settings")   

float v = input.float(40., "% Volatility", group = "Rates Settings") / 100

int histvolper = input.int(22, "Historical Volatility Period", group = "Historical Volatility Settings", tooltip =  "Not used in calculation. This is here for comparison to implied volatility")
string hvoltype = input.string(c2c, "Historical Volatility Type", options = [c2c, gkvol, gkzhvol, rogersatch, ewmavolstr, parkinson], group = "Historical Volatility Settings")

string timein = input.string("Time Now", title = "Time Now Type", options = ["Time Now", "Time Bar", "Trading Day"], group = "Time Intrevals", tooltip = timtoolnow + "; " + timtoolbar + "; " + timetooltrade)
int daysinyear = input.int(252, title = "Days in Year", minval = 1, maxval = 365, group = "Time Intrevals", tooltip = "Typically 252 or 365")
float hoursinday = input.float(24, title = "Hours Per Day", minval = 1, maxval = 24, group = "Time Intrevals", tooltip = "Typically 6.5, 8, or 24")

int thruMonth = input.int(3, title = "Expiry Month", minval = 1, maxval = 12, group = "Expiry Date/Time")
int thruDay = input.int(31, title = "Expiry Day", minval = 1, maxval = 31, group = "Expiry Date/Time")
int thruYear = input.int(2023, title = "Expiry Year", minval = 1970, group = "Expiry Date/Time")
int mins = input.int(0, title = "Expiry Minute", minval = 0, maxval = 60, group = "Expiry Date/Time")
int hours = input.int(9, title = "Expiry Hour", minval = 0, maxval = 24, group = "Expiry Date/Time")
int secs = input.int(0, title = "Expiry Second", minval = 0, maxval = 60, group = "Expiry Date/Time")

string txtsize = input.string("Auto", title = "Expiry Second", options = ["Small", "Normal", "Tiny", "Auto", "Large"], group = "UI Options")

string outsize = switch txtsize
    "Small"=> size.small
    "Normal"=> size.normal
    "Tiny"=> size.tiny
    "Auto"=> size.auto
    "Large"=> size.large

// seconds per year given inputs above
int spyr = math.round(daysinyear * hoursinday * 60 * 60)

// precision calculation miliseconds in time intreval from time equals now
start = timein == "Time Now" ? timenow : timein == "Time Bar" ? time : time_tradingday
finish = timestamp(thruYear, thruMonth, thruDay, hours, mins, secs) 
temp = (finish - start) 
float T = (finish - start) / spyr / 1000

kfl=input.float(0.666, title="* Kaufman's Adaptive MA (KAMA) Only - Fast End", group = "Moving Average Inputs")
ksl=input.float(0.0645, title="* Kaufman's Adaptive MA (KAMA) Only - Slow End", group = "Moving Average Inputs")
amafl = input.int(2, title="* Adaptive Moving Average (AMA) Only - Fast", group = "Moving Average Inputs")
amasl = input.int(30, title="* Adaptive Moving Average (AMA) Only - Slow", group = "Moving Average Inputs")

haclose = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, close)
haopen = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, open)
hahigh = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, high)
halow = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, low)
hamedian = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, hl2)
hatypical = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, hlc3)
haweighted = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, hlcc4)
haaverage = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, ohlc4)

float spot = switch srcin
	"Close" => loxxexpandedsourcetypes.rclose()
	"Open" => loxxexpandedsourcetypes.ropen()
	"High" => loxxexpandedsourcetypes.rhigh()
	"Low" => loxxexpandedsourcetypes.rlow()
	"Median" => loxxexpandedsourcetypes.rmedian()
	"Typical" => loxxexpandedsourcetypes.rtypical()
	"Weighted" => loxxexpandedsourcetypes.rweighted()
	"Average" => loxxexpandedsourcetypes.raverage()
    "Average Median Body" => loxxexpandedsourcetypes.ravemedbody()
	"Trend Biased" => loxxexpandedsourcetypes.rtrendb()
	"Trend Biased (Extreme)" => loxxexpandedsourcetypes.rtrendbext()
	"HA Close" => loxxexpandedsourcetypes.haclose(haclose)
	"HA Open" => loxxexpandedsourcetypes.haopen(haopen)
	"HA High" => loxxexpandedsourcetypes.hahigh(hahigh)
	"HA Low" => loxxexpandedsourcetypes.halow(halow)
	"HA Median" => loxxexpandedsourcetypes.hamedian(hamedian)
	"HA Typical" => loxxexpandedsourcetypes.hatypical(hatypical)
	"HA Weighted" => loxxexpandedsourcetypes.haweighted(haweighted)
	"HA Average" => loxxexpandedsourcetypes.haaverage(haaverage)
    "HA Average Median Body" => loxxexpandedsourcetypes.haavemedbody(haclose, haopen)
	"HA Trend Biased" => loxxexpandedsourcetypes.hatrendb(haclose, haopen, hahigh, halow)
	"HA Trend Biased (Extreme)" => loxxexpandedsourcetypes.hatrendb(haclose, haopen, hahigh, halow)
	"HAB Close" => loxxexpandedsourcetypes.habclose(smthtype, amafl, amasl, kfl, ksl)
	"HAB Open" => loxxexpandedsourcetypes.habopen(smthtype, amafl, amasl, kfl, ksl)
	"HAB High" => loxxexpandedsourcetypes.habhigh(smthtype, amafl, amasl, kfl, ksl)
	"HAB Low" => loxxexpandedsourcetypes.hablow(smthtype, amafl, amasl, kfl, ksl)
	"HAB Median" => loxxexpandedsourcetypes.habmedian(smthtype, amafl, amasl, kfl, ksl)
    "HAB Typical" => loxxexpandedsourcetypes.habtypical(smthtype, amafl, amasl, kfl, ksl)
	"HAB Weighted" => loxxexpandedsourcetypes.habweighted(smthtype, amafl, amasl, kfl, ksl)
	"HAB Average" => loxxexpandedsourcetypes.habaverage(smthtype, amafl, amasl, kfl, ksl)
    "HAB Average Median Body" => loxxexpandedsourcetypes.habavemedbody(smthtype, amafl, amasl, kfl, ksl)
	"HAB Trend Biased" => loxxexpandedsourcetypes.habtrendb(smthtype, amafl, amasl, kfl, ksl)
	"HAB Trend Biased (Extreme)" => loxxexpandedsourcetypes.habtrendbext(smthtype, amafl, amasl, kfl, ksl)
	=> haclose

float hvolout = switch hvoltype
    parkinson => parkinsonvol(histvolper)
    rogersatch => rogerssatchel(histvolper) 
    c2c => closetoclose(math.log(spot / nz(spot[1])), histvolper) 
    gkvol => garmanKlass(histvolper) 
    gkzhvol => gkyzvol(histvolper) 
    ewmavolstr => ewmavol(math.log(spot / nz(spot[1])), histvolper) 

if barstate.islast

    sideout = side == "Long" ? 1 : -1

    price =              CGBlackScholes("p",    OpType, spot, K, T, 0, 0, v) 
    Delta =              CGBlackScholes("d",    OpType, spot, K, T, 0, 0, v) * sideout
    Elasticity =         CGBlackScholes("e",    OpType, spot, K, T, 0, 0, v) * sideout
    Gamma =              CGBlackScholes("g",    OpType, spot, K, T, 0, 0, v) * sideout
    DgammaDvol =         CGBlackScholes("gv",   OpType, spot, K, T, 0, 0, v) * sideout
    GammaP	=            CGBlackScholes("gp",   OpType, spot, K, T, 0, 0, v) * sideout
    Vega	=            CGBlackScholes("v",    OpType, spot, K, T, 0, 0, v) * sideout
    DvegaDvol =          CGBlackScholes("dvdv", OpType, spot, K, T, 0, 0, v) * sideout
    VegaP =              CGBlackScholes("vp",   OpType, spot, K, T, 0, 0, v) * sideout
    Theta =              CGBlackScholes("t",    OpType, spot, K, T, 0, 0, v) * sideout
    DDeltaDvol =         CGBlackScholes("dddv", OpType, spot, K, T, 0, 0, v) * sideout
    Speed =              CGBlackScholes("s",    OpType, spot, K, T, 0, 0, v) * sideout
    DeltaX =             CGBlackScholes("dx",   OpType, spot, K, T, 0, 0, v) * sideout
    RiskNeutralDensity = CGBlackScholes("dxdx", OpType, spot, K, T, 0, 0, v) * sideout
    
    impvolbi = gBlackScholesImpVolBisection(OpType, spot, K, T, 0, 0, price)
    impvolnewt = gImpliedVolatilityNR(OpType, spot, K, T, 0, 0, price, 0.00001)

    var testTable = table.new(position = position.middle_right, columns = 2, rows = 16, bgcolor = color.gray, border_width = 1)

    table.cell(table_id = testTable, column = 0, row = 0,  text = "Asay (1982) Margined Futures Option Pricing Model", bgcolor=color.yellow, text_color = color.black, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 0, row = 1,  text = "Option Type: " + OpType, bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 0, row = 2,  text = "Side: " + side , bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 0, row = 3,  text = "Spot Price: " + str.tostring(spot, format.mintick) , bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 0, row = 4,  text = "Strike Price: " + str.tostring(K, format.mintick), bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)

    table.cell(table_id = testTable, column = 0, row = 5,  text = "% Volatility (annual): " + str.tostring(v * 100, "##.##") + "%", bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 0, row = 6,  text = "Time Now: " + str.format("{0,date,MMMM dd, yyyy - HH:mm:ss}", timenow), bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 0, row = 7,  text = "Expiry Date: " + str.format("{0,date,MMMM dd, yyyy - HH:mm:ss}", finish), bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)

    table.cell(table_id = testTable, column = 0, row = 8, text = "Calculated Values", bgcolor=color.yellow, text_color = color.black, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 0, row = 9, text = "Hist. Volatility Type: " + hvoltype, bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 0, row = 10, text = "Hist. Daily Volatility: " + str.tostring(hvolout * 100, "##.##") + "%", bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 0, row = 11, text = "Hist. Annualized Volatility: " + str.tostring(hvolout * math.sqrt(daysinyear) * 100, "##.##") + "%", bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 0, row = 12, text = "Implied Volatility Calculation", bgcolor=color.yellow, text_color = color.black, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 0, row = 13, text = "Implied Volatility Bisection: " + str.tostring(impvolbi * 100, "##.##") + "%", bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 0, row = 14, text = "Implied Volatility Newton Raphson: " + str.tostring(impvolnewt * 100, "##.##") + "%", bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)

    table.cell(table_id = testTable, column = 1, row = 0, text = "Option Ouputs", bgcolor=color.yellow, text_color = color.black, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 1, row = 1, text = "Price: " + str.tostring(price, format.mintick), bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)

    table.cell(table_id = testTable, column = 1, row = 2, text = "Analytical Greeks", bgcolor=color.yellow, text_color = color.black, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 1, row = 3, text = "Delta Δ: " + str.tostring(Delta, "##.#####"), bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 1, row = 4, text = "Elasticity Λ: " + str.tostring(Elasticity, "##.########"), bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 1, row = 5, text = "Gamma Γ: " + str.tostring(Gamma, "##.########"), bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 1, row = 6, text = "DGammaDvol: " + str.tostring(DgammaDvol, "##.########"), bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 1, row = 7, text = "GammaP Γ: " + str.tostring(GammaP, "##.########"), bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 1, row = 8, text = "Vega: " + str.tostring(Vega, "##.########"), bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 1, row = 9, text = "DVegaDvol: " + str.tostring(DvegaDvol, "##.########"), bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 1, row = 10, text = "VegaP: " + str.tostring(VegaP, "##.########"), bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 1, row = 11, text = "Theta Θ (1 day): " + str.tostring(Theta, "##.########"), bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 1, row = 12, text = "DDeltaDvol: " + str.tostring(DDeltaDvol, "##.########"), bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 1, row = 13, text = "Speed: " + str.tostring(Speed, "##.########"), bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 1, row = 14, text = "Strike Delta: " + str.tostring(DeltaX, "##.########"), bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 1, row = 15, text = "Risk Neutral Density: " + str.tostring(RiskNeutralDensity, "##.########"), bgcolor=darkGreenColor, text_color = color.white, text_size = outsize, text_halign = text.align_left)
