Awesome Oscillator Plus
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © OskarGallard

//@version=5
indicator(title="Awesome Oscillator Plus", shorttitle="AO [+]", timeframe="", timeframe_gaps=true)
// Input - Awesome Oscillator {
group_ao = "Awesome Oscillator"
show_ao  = input.bool(true,    "Show AO",   inline="inputAO", group = group_ao)
type_osc = input.string("SMA", "MA Type",   inline="inputAO", group = group_ao, options=["JMA", "NWMA", "VAMA", "SMA", "EMA", "WMA", "DWMA", "Wild", "LSMA", "ALMA", "VWMA", "DVWMA", "HMA", "COVWMA", "FRAMA", "KAMA", "Zero Lag", "RMA", "DEMA", "TEMA", "Median"])
ao_src   = input.source(hl2,   "   Source", inline="inputAO", group = group_ao)
show_sig = input.bool(false,    "Show Signal", inline="signal", group = group_ao)
type_sig = input.string("NWMA", "MA Type", inline="signal", group = group_ao, options=["JMA", "NWMA", "VAMA", "SMA", "EMA", "WMA", "DWMA", "Wild", "LSMA", "ALMA", "VWMA", "DVWMA", "HMA", "COVWMA", "FRAMA", "KAMA", "Zero Lag", "RMA", "DEMA", "TEMA", "Median"])
len_sig  = input.int(15,        "Length", 1, inline="signal", group = group_ao)
show_saucer_bull = input.bool(true, "Bullrish Saucer   ", inline="saucer", group = group_ao)
show_saucer_bear = input.bool(true, "Bearish Saucer",  inline="saucer", group = group_ao)
len_HL       = input.int(150,    "Highest/Lowest Period", 5, inline="HL", group = group_ao)
show_Highest = input.bool(false, "Show Highest ", inline="HL", group = group_ao)
show_Lowest  = input.bool(false, "Show Lowest",  inline="HL", group = group_ao)
// VAMA factor
factor = input.float(0.67, "Factor (if VAMA)", minval = 0.01, step = 0.1, group = group_ao)
// JMA 
phaseJ = input.int(50, "Phase (if JMA)", group = group_ao)
powerJ = input.int(2,  "Power (if JMA)", group = group_ao)
// Volume Type
voltype = input.string("Default", "Volume Type (if VWMA)", options=["Default", "Tick"], group = group_ao)
// LSMA Offset
loff = input.int(0, "Offset (if LSMA)", minval=0, group = group_ao)
// ALMA Offset and Sigma
offs  = input.float(0.85, "Offset (if ALMA)", step=0.01, minval=0, group = group_ao)
sigma = input.int(6,      "Sigma (if ALMA)", minval=0, group = group_ao) 
// FRAMA Coefficient
w = input.float(-4.6, "Coefficient (if FRAMA)", group = group_ao)
// KAMA Smoothing Constant
fastK = input.float(0.666,  "Smoothing Constant Fast End (if KAMA)", step=0.001,  group = group_ao)
slowK = input.float(0.0645, "Smoothing Constant Slow End (if KAMA)", step=0.0001, group = group_ao)
// }
// Types of moving averages{
// JMA - Jurik Moving Average of @everget
jma(src, length, power, phase) => 
    phaseRatio = phase < -100 ? 0.5 : phase > 100 ? 2.5 : phase / 100 + 1.5
    beta = 0.45 * (length - 1) / (0.45 * (length - 1) + 2)
    alpha = math.pow(beta, power)
    Jma = 0.0
    e0 = 0.0
    e0 := (1 - alpha) * src + alpha * nz(e0[1])
    e1 = 0.0
    e1 := (src - e0) * (1 - beta) + beta * nz(e1[1])
    e2 = 0.0
    e2 := (e0 + phaseRatio * e1 - nz(Jma[1])) * math.pow(1 - alpha, 2) + math.pow(alpha, 2) * nz(e2[1])
    Jma := e2 + nz(Jma[1])
    Jma

// KAMA - Kaufman's Adaptive Moving Average
kama(x, t)=>
    dist   = math.abs(x[0] - x[1])
    signal_x = math.abs(x - x[t])
    noise  = math.sum(dist, t)
    effr   = noise!=0 ? signal_x/noise : 1
    sc     = math.pow(effr*(fastK - slowK) + slowK,2)
    KAma   = x
    KAma   := nz(KAma[1]) + sc*(x - nz(KAma[1]))
    KAma

// COVWMA - Coefficient of Variation Weighted Moving Average of @DonovanWall
covwma(a, b) =>
    cov    = ta.stdev(a, b) / ta.sma(a, b)
    cw     = a*cov
    covwma = math.sum(cw, b) / math.sum(cov, b)

// FRAMA - Fractal Adaptive Moving Average of @DonovanWall
frama(a, b) =>
    frama = 0.0
    n3    = (ta.highest(high, b) - ta.lowest(low, b))/b
    hd2   = ta.highest(high, b/2)
    ld2   = ta.lowest(low, b/2)
    n2    = (hd2 - ld2)/(b/2)
    n1    = (hd2[b/2] - ld2[b/2])/(b/2)
    dim   = (n1 > 0) and (n2 > 0) and (n3 > 0) ? (math.log(n1 + n2) - math.log(n3))/math.log(2) : 0
    alpha = math.exp(w*(dim - 1))
    sc    = (alpha < 0.01 ? 0.01 : (alpha > 1 ? 1 : alpha))
    frama := ta.cum(1)<=2*b ? a : (a*sc) + nz(frama[1])*(1 - sc)
    frama

// ZLEMA: Zero Lag
zema(_src, _len) =>
    alpha = (_len - 1) / 2
    zlema0 = _src + _src - _src[alpha]
    zlemaF = ta.ema(zlema0, _len)
    zlemaF

// VWMA
VWMA(x, t) =>
    tick     = syminfo.mintick
    rng      = close - open
    tickrng  = tick
    tickrng := math.abs(rng) < tick ? nz(tickrng[1]) : rng
    tickvol  = math.abs(tickrng) / tick
    vol      = voltype == "Default" ? (volume == na ? tickvol : volume) : tickvol
    vmp      = x * vol
    VWMA     = math.sum(vmp, t) / math.sum(vol, t)
    VWMA

// VAMA - Volume Adjusted Moving Average of @allanster
vama(_src,_len,_fct,_rul,_nvb) =>                             // vama(source,length,factor,rule,sample)
    tvb = 0, tvb := _nvb == 0 ? nz(tvb[1]) + 1 : _nvb         // total volume bars used in sample            
    tvs = _nvb == 0 ? ta.cum(volume) : math.sum(volume, _nvb) // total volume in sample
    v2i = volume / ((tvs / tvb) * _fct)                       // ratio of volume to increments of volume                                                  
    wtd = _src*v2i                                            // weighted prices 
    nmb = 1                                                   // initialize number of bars summed back               
    wtdSumB = 0.0                                             // initialize weighted prices summed back
    v2iSumB = 0.0                                             // initialize ratio of volume to increments of volume summed back
    for i = 1 to _len * 10                                    // set artificial cap for strict to VAMA length * 10 to help reduce edge case timeout errors
        strict = _rul ? false : i == _len                     // strict rule N bars' v2i ratios >= vama length, else <= vama length
        wtdSumB := wtdSumB + nz(wtd[i-1])                     // increment number of bars' weighted prices summed back
        v2iSumB := v2iSumB + nz(v2i[i-1])                     // increment number of bars' v2i's summed back
        if v2iSumB >= _len or strict                          // if chosen rule met
            break                                             // break (exit loop)
        nmb := nmb + 1                                        // increment number of bars summed back counter
    nmb                                                       // number of bars summed back to fulfill volume requirements or vama length
    wtdSumB                                                   // number of bars' weighted prices summed back
    v2iSumB                                                   // number of bars' v2i's summed back
    vama = (wtdSumB - (v2iSumB - _len) * _src[nmb]) / _len    // volume adjusted moving average

// New WMA
NWMA(_src, _n1) =>
    fast   = int(_n1 / 2)
    lambda = _n1 / fast
    alpha  = lambda * (_n1 - 1) / (_n1 - lambda)
    ma1    = ta.wma(_src, _n1)
    ma2    = ta.wma(ma1, fast)
    nwma   = (1 + alpha) * ma1 - alpha * ma2
    
// Function to know the type of MA
ma(MAType, MASource, MAPeriod) =>
    if MAPeriod > 0
        if MAType == "SMA"
            ta.sma(MASource, MAPeriod)
        else if MAType == "LSMA"
            ta.linreg(MASource, MAPeriod, loff)
        else if MAType == "EMA"
            ta.ema(MASource, MAPeriod)
        else if MAType == "WMA"
            ta.wma(MASource, MAPeriod)
        else if MAType == "RMA"
            ta.rma(MASource, MAPeriod)
        else if MAType == "HMA"
            ta.hma(MASource, MAPeriod)
        else if MAType == "DEMA"
            e = ta.ema(MASource, MAPeriod)
            2 * e - ta.ema(e, MAPeriod)
        else if MAType == "TEMA"
            e = ta.ema(MASource, MAPeriod)
            3 * (e - ta.ema(e, MAPeriod)) + ta.ema(ta.ema(e, MAPeriod), MAPeriod)
        else if MAType == "Median"
            ta.median(MASource, MAPeriod)
        else if MAType == "VWMA"
            VWMA(MASource, MAPeriod)
        else if MAType == "ALMA"
            ta.alma(MASource, MAPeriod, offs, sigma)
        else if MAType == "KAMA"
            kama(MASource, MAPeriod)
        else if MAType == "Wild"
            wild = MASource
            wild := nz(wild[1]) + (MASource - nz(wild[1])) / MAPeriod
        else if MAType == "JMA"
            jma(MASource, MAPeriod, powerJ, phaseJ)
        else if MAType == "DWMA" // Double Weighted Moving Average
            ta.wma(ta.wma(MASource, MAPeriod), MAPeriod)
        else if MAType == "DVWMA" // Double Volume-Weighted Moving Average
            ta.vwma(ta.vwma(MASource, MAPeriod), MAPeriod)
        else if MAType == "COVWMA"
            covwma(MASource, MAPeriod)
        else if MAType == "FRAMA"
            frama(MASource, MAPeriod)
        else if MAType == "Zero Lag"
            zema(MASource, MAPeriod)
        else if MAType == "VAMA" 
            vama(MASource, MAPeriod, factor, true, 0)
        else if MAType == "NWMA"
            NWMA(MASource, MAPeriod)
// }
// Input - Moving Average Convergence Divergence{
group_macd = "Moving Average Convergence Divergence"
tip_macd   = "This factor is used to increase the size of the graph.-"
show_macd  = input.bool(true,    "Show MACD Line", inline="show", group = group_macd)
show_cross = input.bool(true,    "Show Crossovers", inline="show", group = group_macd)
show_barC  = input.bool(false,   "Show color bars", inline="show", group = group_macd)
show_hist  = input.bool(false,   "Show MACD Histogram", inline="line2", group = group_macd)
exp_factor = input.float(5.5,    "Expansion Factor", 1, 10, inline="line2", group = group_macd, tooltip = tip_macd)
macd_src   = input.source(close, "Source", group = group_macd)
macd_fast  = input.int(13,       "Fast MA Length",   1, group = group_macd)
macd_slow  = input.int(21,       "Slow MA Length",   2, group = group_macd)
macd_sig   = input.int(8,        "Signal Length",    1, group = group_macd)
// }
// Input - Divergences{
group_divergences = "Divergences For Awesome Oscillator"
plotBull         = input.bool(true,  "Plot Bullish   ",     inline="bull", group=group_divergences)
plotHiddenBull   = input.bool(false, "Plot Hidden Bullish", inline="bull", group=group_divergences)
plotBear         = input.bool(true,  "Plot Bearish   ",     inline="bear", group=group_divergences)
plotHiddenBear   = input.bool(false, "Plot Hidden Bearish", inline="bear", group=group_divergences)
lbR              = input.int(5,      "Pivot Lookback Right",  group=group_divergences)
lbL              = input.int(5,      "Pivot Lookback Left",   group=group_divergences)
rangeUpper       = input.int(60,     "Max of Lookback Range", group=group_divergences)
rangeLower       = input.int(5,      "Min of Lookback Range", group=group_divergences)
// }
// MACD - Moving Average Convergence Divergence{
[macd_line, macd_signal, macd_hist] = ta.macd(macd_src, macd_fast, macd_slow, macd_sig)

cross_up = macd_line < 0 and ta.crossover(macd_line, macd_signal)
cross_dn = macd_line > 0 and ta.crossunder(macd_line, macd_signal)
// }
// AO - Awesome Oscillator {
ma_fast = ma(type_osc, ao_src, 5)  
ma_slow = ma(type_osc, ao_src, 34)
ao = ma_fast - ma_slow
signal_ao = ma(type_sig, ao, len_sig)
diff = ao - ao[1]
forma_v = math.abs(ao[2]) > math.abs(ao[1]) 
highest_55 = ta.highest(ao, len_HL)
lowest_55  = ta.lowest(ao, len_HL)
ao_Highest = ao > 0 and ao >= highest_55 
ao_Lowest  = ao < 0 and ao <= lowest_55  

bullish_saucer = ao[2] > 0 and ao[1] > 0 and diff[3] < 0 and diff[2] < 0 and forma_v and ta.crossover(diff, 0)  
bearish_saucer = ao[2] < 0 and ao[1] < 0 and diff[3] > 0 and diff[2] > 0 and forma_v and ta.crossunder(diff, 0) 
// }
// Plots - AO {
rojo  = color.new(#FF6347, 0) // #96000E
verde = color.new(#32CD32, 0) // #00963D

plot(show_ao ? ao : na, "Awesome Oscillator", diff <= 0 ? rojo : verde, 3, plot.style_histogram)
plot(show_Highest and ao_Highest ? ao : na, "AO Highest", #4774FF, 2, plot.style_circles)
plot(show_Lowest  and ao_Lowest  ? ao : na, "AO Lowest",  #4774FF, 2, plot.style_circles)
plot(show_sig ? signal_ao : na, "AO Signal", color.silver)

plotshape(show_saucer_bull and bullish_saucer, color=verde, style=shape.triangleup,   title="Bullish saucer", location=location.bottom)
plotshape(show_saucer_bear and bearish_saucer, color=rojo,  style=shape.triangledown, title="Bearish saucer", location=location.top)

bgcolor(show_saucer_bull and bullish_saucer ? color.new(verde, 85) : na, title="Bullish saucer")
bgcolor(show_saucer_bear and bearish_saucer ? color.new(rojo, 85)  : na, title="Bearish saucer")
// }
// Plots - MACD {
cian = color.new(#38F5E9, 0)
cian_oscuro = color.new(#1C7A74, 0)
pink = color.new(#F7709D, 0)
pink_oscuro = color.new(#7B384E, 0)
color_hist = macd_hist > 0 ? color.new(cian, 80) : color.new(pink, 80)
color_up = macd_line > macd_signal ? cian : cian_oscuro
color_dn = macd_line < macd_signal ? pink : pink_oscuro
color_macd = macd_line > 0 ? color_up : color_dn
color_bar = macd_line > 0 ? color_up : color_dn
macd_exp = macd_line * exp_factor
hist_exp = macd_hist * exp_factor * 2
plot(show_hist ? hist_exp : na, "MACD Histogram", color_hist, 1, plot.style_columns)
plot(show_macd ? macd_exp : na, "MACD Line", color_macd, 2)
plot(show_cross and cross_up ? macd_exp : na, "MACD Cross Up",   color.new(cian, 55), 5, plot.style_circles)
plot(show_cross and cross_dn ? macd_exp : na, "MACD Cross Down", color.new(pink, 55), 5, plot.style_circles)
barcolor(show_barC ? color_bar : na, title = "MACD Color Bars")
// }
// Divergences {
osc = show_ao ? ao : na
bearColor = rojo
bullColor = verde
hiddenBullColor = color.new(bullColor, 80)
hiddenBearColor = color.new(bearColor, 80)
textColor = color.white
textColor2 = #000000
noneColor = color.new(color.white, 100)

plFound = na(ta.pivotlow(osc, lbL, lbR))  ? false : true
phFound = na(ta.pivothigh(osc, lbL, lbR)) ? false : true
_inRange(cond) =>
	bars = ta.barssince(cond == true)
	rangeLower <= bars and bars <= rangeUpper

//------------------------------------------------------------------------------
// Regular Bullish
// Osc: Higher Low

oscHL = osc[lbR] > ta.valuewhen(plFound, osc[lbR], 1) and _inRange(plFound[1])

// Price: Lower Low

priceLL = low[lbR] < ta.valuewhen(plFound, low[lbR], 1)
bullCond = plotBull and priceLL and oscHL and plFound

plot(
     plFound ? osc[lbR] : na,
     offset=-lbR,
     title="Regular Bullish",
     linewidth=2,
     color=(bullCond ? bullColor : noneColor)
     )

plotshape(
	 bullCond ? osc[lbR] : na,
	 offset=-lbR,
	 title="Regular Bullish Label",
	 text="R",
	 style=shape.labelup,
	 location=location.absolute,
	 color=bullColor,
	 textcolor=textColor2
	 )
	 
//------------------------------------------------------------------------------
// Hidden Bullish
// Osc: Lower Low

oscLL = osc[lbR] < ta.valuewhen(plFound, osc[lbR], 1) and _inRange(plFound[1])

// Price: Higher Low

priceHL = low[lbR] > ta.valuewhen(plFound, low[lbR], 1)
hiddenBullCond = plotHiddenBull and priceHL and oscLL and plFound

plot(
	 plFound ? osc[lbR] : na,
	 offset=-lbR,
	 title="Hidden Bullish",
	 linewidth=2,
	 color=(hiddenBullCond ? hiddenBullColor : noneColor)
	 )

plotshape(
	 hiddenBullCond ? osc[lbR] : na,
	 offset=-lbR,
	 title="Hidden Bullish Label",
	 text="H",
	 style=shape.labelup,
	 location=location.absolute,
	 color=bullColor,
	 textcolor=textColor2
	 )

//------------------------------------------------------------------------------
// Regular Bearish
// Osc: Lower High

oscLH = osc[lbR] < ta.valuewhen(phFound, osc[lbR], 1) and _inRange(phFound[1])

// Price: Higher High

priceHH = high[lbR] > ta.valuewhen(phFound, high[lbR], 1)

bearCond = plotBear and priceHH and oscLH and phFound

plot(
	 phFound ? osc[lbR] : na,
	 offset=-lbR,
	 title="Regular Bearish",
	 linewidth=2,
	 color=(bearCond ? bearColor : noneColor)
	 )

plotshape(
	 bearCond ? osc[lbR] : na,
	 offset=-lbR,
	 title="Regular Bearish Label",
	 text="R",
	 style=shape.labeldown,
	 location=location.absolute,
	 color=bearColor,
	 textcolor=textColor
	 )

//------------------------------------------------------------------------------
// Hidden Bearish
// Osc: Higher High

oscHH = osc[lbR] > ta.valuewhen(phFound, osc[lbR], 1) and _inRange(phFound[1])

// Price: Lower High

priceLH = high[lbR] < ta.valuewhen(phFound, high[lbR], 1)

hiddenBearCond = plotHiddenBear and priceLH and oscHH and phFound

plot(
	 phFound ? osc[lbR] : na,
	 offset=-lbR,
	 title="Hidden Bearish",
	 linewidth=2,
	 color=(hiddenBearCond ? hiddenBearColor : noneColor)
	 )

plotshape(
	 hiddenBearCond ? osc[lbR] : na,
	 offset=-lbR,
	 title="Hidden Bearish Label",
	 text="H",
	 style=shape.labeldown,
	 location=location.absolute,
	 color=bearColor,
	 textcolor=textColor
	 )
// }
// Alerts {
alertcondition(barstate.islast ? cross_up[1] : cross_up, title="MACD Long",  message="MACD: Probable Long\n{{exchange}}:{{ticker}}->\nPrice = {{close}},\nTime = {{time}}")
alertcondition(barstate.islast ? cross_up[1] : cross_dn, title="MACD Short", message="MACD: Probable Short\n{{exchange}}:{{ticker}}->\nPrice = {{close}},\nTime = {{time}}")

alertcondition(bullish_saucer, title="AO - Bullish Saucer", message="AO: Probable Long\n A red bar, followed by a smaller red bar, followed by a green bar indicates a Bullish Saucer.-")
alertcondition(bearish_saucer, title="AO - Bearish Saucer", message="AO: Probable Short\n A green bar, followed by a samller green bar, followed a red bar indicates a Bearish Saucer.-")
// }
 