Copy
//@version=5
// ─────────────────────────────────────────────────────────────────────────────
// Stratomation VWAP Extreme Cloud
// Chart-side mirror of the VWAP Bot control rules (strategy v1.3.0).
// Built for a 5-minute chart with EXTENDED HOURS OFF.
// ─────────────────────────────────────────────────────────────────────────────
indicator("Stratomation VWAP Extreme Cloud", shorttitle="STR VWAP CLOUD", overlay=true, max_lines_count=500, max_labels_count=500)
// ═══ INPUTS ═══
gS = "Session"
sessTime = input.session("0930-1600", "Regular session", group=gS)
tzIn = input.string("America/New_York", "Timezone", group=gS)
gZ = "Cloud geometry"
noiseSig = input.float(1.0, "Noise sigma", step=0.1, group=gZ)
exLoSig = input.float(2.0, "Extreme sigma (inner)", step=0.1, group=gZ)
exHiSig = input.float(3.0, "Extension sigma (outer)", step=0.1, group=gZ)
gT = "Trend"
atrLen = input.int(14, "ATR period", minval=1, group=gT)
rgLook = input.int(12, "Regime lookback bars", minval=3, group=gT)
rgSlopeB = input.int(6, "Slope bars", minval=1, group=gT)
rgPers = input.float(0.70,"Persistence min", step=0.01, group=gT)
rgSlopeAtr = input.float(0.10,"Slope / ATR min", step=0.01, group=gT)
rgEff = input.float(0.35,"Efficiency min", step=0.01, group=gT)
rgCross = input.int(2, "Max VWAP crossings", minval=0, group=gT)
swingLook = input.int(24, "Pre-pullback swing window",minval=2, group=gT)
gE = "Entry"
trigMode = input.string("pattern_breakout", "Trigger mode", options=["pattern_breakout","all_breakout","next_open"], group=gE)
offTicks = input.int(1, "Breakout offset (ticks)", minval=0, group=gE)
maxWait = input.int(12, "Max wait bars (0 = none)", minval=0, group=gE)
tickSz = input.float(0.01,"Tick size", step=0.01, group=gE)
bufMult = input.float(0.10,"Stop buffer x ATR", step=0.01, group=gE)
pinMin = input.float(0.50,"Pin wick/range min", step=0.01, group=gE)
pinMax = input.float(0.67,"Pin wick/range max", step=0.01, group=gE)
oneSide = input.bool(true, "One setup per cloud side per session", group=gE)
autoFlat = input.bool(true, "No new entry on the session's last bar", group=gE)
gD = "Display"
showBands = input.bool(true, "Bands and clouds", group=gD)
showTint = input.bool(true, "Trend regime tint", group=gD)
showLevels = input.bool(true, "Stop and target lines", group=gD)
showTable = input.bool(true, "Status table", group=gD)
// ═══ SESSION VWAP AND VOLUME-WEIGHTED SIGMA ═══
inSess = not na(time(timeframe.period, sessTime, tzIn))
newSess = inSess and not nz(inSess[1], false)
var float sumPV = 0.0
var float sumV = 0.0
var float sumPV2 = 0.0
tp = (high + low + close) / 3.0
if newSess
sumPV := 0.0
sumV := 0.0
sumPV2 := 0.0
if inSess and volume > 0
sumPV := sumPV + tp * volume
sumV := sumV + volume
sumPV2 := sumPV2 + tp * tp * volume
vwapV = sumV > 0 ? sumPV / sumV : na
sigma = sumV > 0 ? math.sqrt(math.max(sumPV2 / sumV - vwapV * vwapV, 0.0)) : na
// ═══ WILDER ATR ON IN-SESSION BARS, CARRIED ACROSS SESSIONS ═══
var float pcl = na
var float atrV = na
var int trN = 0
var float trSum = 0.0
if inSess
trv = na(pcl) ? high - low : math.max(high - low, math.max(math.abs(high - pcl), math.abs(low - pcl)))
pcl := close
if na(atrV)
trN := trN + 1
if trN > 1
trSum := trSum + trv
if trN == atrLen + 1
atrV := trSum / atrLen
else
atrV := (atrV * (atrLen - 1) + trv) / atrLen
// ═══ BAND HELPERS (explicit bands, so a frozen set can be gated against) ═══
lvlB(v, s, k) => v + k * s
zoneOfB(price, v, s) =>
int z = 0
if price > lvlB(v, s, exHiSig)
z := 3
else if price >= lvlB(v, s, exLoSig)
z := 2
else if price > lvlB(v, s, noiseSig)
z := 1
else if price >= lvlB(v, s, -noiseSig)
z := 0
else if price > lvlB(v, s, -exLoSig)
z := -1
else if price >= lvlB(v, s, -exHiSig)
z := -2
else
z := -3
z
inCloudB(price, up, v, s) => up ? price >= lvlB(v, s, exLoSig) : price <= lvlB(v, s, -exLoSig)
execOkB(price, up, v, s) => not inCloudB(price, up, v, s) and zoneOfB(price, v, s) != 0 and not inCloudB(price, not up, v, s)
lvl(k) => lvlB(vwapV, sigma, k)
zoneNow(price) => zoneOfB(price, vwapV, sigma)
inCloudNow(p, up) => inCloudB(p, up, vwapV, sigma)
// family: 1 pullback | 2 extension | na not tradeable
familyFor(reg, up) => na(reg) ? na : reg == 1 ? (up ? 2.0 : 1.0) : reg == -1 ? (up ? 1.0 : 2.0) : na
tgtPrimary(fam, long, v, s) => fam == 1 ? (long ? lvlB(v, s, 2) : lvlB(v, s, -2)) : na
tgtT1(fam, long, v, s) => fam == 2 ? (long ? lvlB(v, s, -1) : lvlB(v, s, 1)) : na
expectedRB(fam, long, entry, dist, v, s) =>
float r = na
if not na(fam) and dist > 0
sign = long ? 1.0 : -1.0
r := fam == 1 ? (tgtPrimary(fam, long, v, s) - entry) * sign / dist : (0.5 * (tgtT1(fam, long, v, s) - entry) + 0.5 * (v - entry)) * sign / dist
r
behindB(fam, long, price, v, s) =>
sign = long ? 1.0 : -1.0
na(fam) ? true : fam == 1 ? (tgtPrimary(fam, long, v, s) - price) * sign <= 0 : ((tgtT1(fam, long, v, s) - price) * sign <= 0 or (v - price) * sign <= 0)
// ═══ REGIME CLASSIFIER: the five trend tests ═══
// 1 uptrend | -1 downtrend | 0 balanced | 2 mixed | na undefined
var array<float> cHist = array.new_float(0)
var array<float> vHist = array.new_float(0)
classify() =>
float res = na
n = rgLook
if not na(atrV) and atrV > 0 and array.size(cHist) >= n and n - 1 - rgSlopeB >= 0
base = array.size(cHist) - n
int upN = 0
int dnN = 0
int cross = 0
int prev = 0
int lastSide = 0
float path = 0.0
for i = 0 to n - 1
c = array.get(cHist, base + i)
v = array.get(vHist, base + i)
s = c > v ? 1 : c < v ? -1 : 0
upN := upN + (s == 1 ? 1 : 0)
dnN := dnN + (s == -1 ? 1 : 0)
if s != 0
cross := cross + (prev != 0 and s != prev ? 1 : 0)
prev := s
if i > 0
path := path + math.abs(c - array.get(cHist, base + i - 1))
if i == n - 1
lastSide := s
pu = upN / float(n)
pd = dnN / float(n)
slope = array.get(vHist, base + n - 1) - array.get(vHist, base + n - 1 - rgSlopeB)
sNorm = math.abs(slope) / atrV
eff = path > 0 ? math.abs(array.get(cHist, base + n - 1) - array.get(cHist, base)) / path : 0.0
upOk = pu >= rgPers and sNorm >= rgSlopeAtr and slope > 0 and eff >= rgEff and cross <= rgCross and lastSide == 1
dnOk = pd >= rgPers and sNorm >= rgSlopeAtr and slope < 0 and eff >= rgEff and cross <= rgCross and lastSide == -1
res := upOk ? 1.0 : dnOk ? -1.0 : (pu < rgPers and pd < rgPers and eff < rgEff) ? 0.0 : 2.0
res
// ═══ REJECTION-CANDLE SHAPE ═══
touchesExtreme(up) => inCloudNow(up ? high : low, up)
wickFrac(up) =>
rng = high - low
rng <= 0 ? na : (up ? high - math.max(open, close) : math.min(open, close) - low) / rng
// prev is the previous ACCEPTED bar of the SAME session; there is none on a session's first bar
engulfs(up) =>
bool r = false
if not newSess and nz(inSess[1], false)
r := up ? (close < open and close[1] > open[1] and open >= close[1] and close <= open[1]) : (close > open and close[1] < open[1] and open <= close[1] and close >= open[1])
r
patternOf(up) =>
reaches = touchesExtreme(up)
eng = reaches and engulfs(up)
wf = wickFrac(up)
pin = not na(wf) and wf >= pinMin and wf <= pinMax and reaches
eng and pin ? "pin_and_engulfing" : eng ? "engulfing" : pin ? "pin" : (reaches and not na(wf) and wf > pinMax) ? "pin_wide" : "none"
isBreakoutPattern(p) => p == "engulfing" or p == "pin" or p == "pin_and_engulfing"
// ═══ STATE ═══
// 0 IDLE | 1 EXCURSION | 2 ENTRY_GATES (next-open fill pending) | 3 BREAKOUT_WAIT | 4 COOLDOWN
var int stState = 0
var bool stUp = false
var float stExtreme = na
var int stBars = 0
var bool stExtension = false
var float stRegime = na
var int stAttempts = 0
var bool cdLong = false
var bool cdUp = false
var float cdFam = na
var float cdStop = na
var float cdExtreme = na
var float cdVwap = na
var float cdSigma = na
var float cdLevel = na
var string cdPattern = "none"
var int boBars = 0
var float posEntry = na
var bool posT1 = false
var bool firedUpper = false
var bool firedLower = false
var array<float> shReg = array.new_float(0)
var array<float> shHi = array.new_float(0)
var array<float> shLo = array.new_float(0)
clearWindows() =>
array.clear(shReg)
array.clear(shHi)
array.clear(shLo)
remember(reg) =>
array.push(shReg, reg)
array.push(shHi, high)
array.push(shLo, low)
while array.size(shReg) > swingLook
array.shift(shReg)
array.shift(shHi)
array.shift(shLo)
// The pullback origin: highest high (lower cloud) or lowest low (upper cloud) among the idle bars
// BEFORE the entering bar, ties to the most recent. Its regime is the frozen pre-excursion trend.
prepullbackRegime(up) =>
float reg = na
n = array.size(shReg) - 1
if n > 0
int idx = 0
float best = up ? array.get(shLo, 0) : array.get(shHi, 0)
if n > 1
for i = 1 to n - 1
v = up ? array.get(shLo, i) : array.get(shHi, i)
if up ? v <= best : v >= best
best := v
idx := i
r = array.get(shReg, idx)
reg := (r == 1 or r == -1) ? r : na
reg
// Did this bar reach `level`? favorable=true for targets, false for stops.
touchedP(level, favorable, long) => long ? (favorable ? high >= level : low <= level) : (favorable ? low <= level : high >= level)
resolved() => zoneNow(close) == 0 or (stUp ? close < vwapV : close > vwapV)
// ═══ PER-BAR EVENTS ═══
bool evArm = false
bool evFire = false
bool evReject = false
bool evVoid = false
bool evExit = false
string evText = ""
float evR = na
if newSess
firedUpper := false
firedLower := false
stState := 0
stAttempts := 0
stExtension := false
stExtreme := na
boBars := 0
array.clear(cHist)
array.clear(vHist)
clearWindows()
// The engine appends every accepted bar's close and VWAP BEFORE classifying, so the regime window
// includes the current bar and `sides[-1]` is this bar's side.
if inSess and not na(vwapV) and volume > 0
array.push(cHist, close)
array.push(vHist, vwapV)
while array.size(cHist) > rgLook
array.shift(cHist)
array.shift(vHist)
regime = classify()
lastBarOfSession = autoFlat and session.islastbar_regular
// A bar with no usable band or no ATR is skipped whole, as the engine skips it.
if inSess and not na(sigma) and sigma > 0 and not na(atrV) and volume > 0
bool handled = false
bool wasOpen = stState == 5
if stState == 0 or stState == 4
remember(regime)
// ── BREAKOUT_WAIT: one bar of a resting stop-entry order ──
if stState == 3
handled := true
boBars := boBars + 1
if lastBarOfSession
evVoid := true
evText := "session end"
stState := 4
else
breached = cdLong ? low < cdExtreme : high > cdExtreme
crossed = cdLong ? high >= cdLevel : low <= cdLevel
if breached
evVoid := true
evText := "extreme breached"
stState := 4
else if crossed
fillP = cdLong ? math.max(cdLevel, open) : math.min(cdLevel, open)
dist = cdLong ? fillP - cdStop : cdStop - fillP
// Stage 2 on a breakout fill reads the FILL bar's bands (control: stage2_bands = fill)
okZone = execOkB(fillP, cdUp, vwapV, sigma)
okTgt = not behindB(cdFam, cdLong, fillP, vwapV, sigma)
evR := expectedRB(cdFam, cdLong, fillP, dist, vwapV, sigma)
if okZone and okTgt and dist > 0
evFire := true
evText := "fill " + str.tostring(fillP, format.mintick)
posEntry := fillP
posT1 := false
stState := 5
if cdUp
firedUpper := true
else
firedLower := true
else
evReject := true
evText := not okZone ? "executable_zone" : not okTgt ? "target_inverted" : "stop_geometry"
stState := 4
else if inCloudNow(close, cdUp)
evVoid := true
evText := "re-entered cloud"
stState := 4
else if zoneNow(close) == 0
evVoid := true
evText := "drifted into noise"
stState := 4
else if maxWait > 0 and boBars >= maxWait
evVoid := true
evText := "expired"
stState := 4
// ── ENTRY_GATES: the next-open fill of an ordinary rejection ──
else if stState == 2
handled := true
fillP = open
dist = cdLong ? fillP - cdStop : cdStop - fillP
// the next-open path keeps the bands frozen at the rejection close
okZone = execOkB(fillP, cdUp, cdVwap, cdSigma)
okTgt = not behindB(cdFam, cdLong, fillP, cdVwap, cdSigma)
evR := expectedRB(cdFam, cdLong, fillP, dist, cdVwap, cdSigma)
if okZone and okTgt and dist > 0
evFire := true
evText := "fill " + str.tostring(fillP, format.mintick)
posEntry := fillP
posT1 := false
stState := 5
if cdUp
firedUpper := true
else
firedLower := true
else
evReject := true
evText := not okZone ? "executable_zone" : not okTgt ? "target_inverted" : "stop_geometry"
stState := 4
// ── OPEN position: one management pass, stop first, no ratchet in the control ──
if stState == 5
string exr = ""
if lastBarOfSession
exr := "session flat"
else if touchedP(cdStop, false, cdLong)
exr := posT1 ? "breakeven stop" : "stop"
else
if cdFam == 1
if touchedP(cdLong ? lvl(2) : lvl(-2), true, cdLong)
exr := "target"
else
if not posT1 and touchedP(cdLong ? lvl(-1) : lvl(1), true, cdLong)
posT1 := true
cdStop := posEntry
if posT1 and touchedP(vwapV, true, cdLong)
exr := "target VWAP"
if exr != ""
evExit := true
evText := exr
stState := 4
else if wasOpen
handled := true
if not handled
// ── COOLDOWN resolves back to IDLE, wiping both windows ──
if stState == 4 and resolved()
stState := 0
stAttempts := 0
stExtension := false
stExtreme := na
clearWindows()
remember(regime)
// ── IDLE: does this bar reach into an extreme cloud? ──
if stState == 0
up = high >= lvl(exLoSig)
down = low <= lvl(-exLoSig)
if up and down
up := (high - vwapV) >= (vwapV - low)
down := not up
if up or down
stState := 1
stUp := up
stExtreme := up ? high : low
stBars := 1
stExtension := false
stAttempts := 0
stRegime := prepullbackRegime(up)
else if stState == 1
stBars := stBars + 1
stExtreme := stUp ? math.max(stExtreme, high) : math.min(stExtreme, low)
// ── rejection test, on the entering bar and every excursion bar after it ──
if stState == 1
if stUp ? stExtreme > lvl(exHiSig) : stExtreme < lvl(-exHiSig)
stExtension := true
if not inCloudNow(close, stUp)
long = not stUp
fam = familyFor(stRegime, stUp)
buf = math.max(tickSz, bufMult * atrV)
stop = long ? stExtreme - buf : stExtreme + buf
pat = patternOf(stUp)
dist = long ? close - stop : stop - close
sideUsed = stUp ? firedUpper : firedLower
stAttempts := stAttempts + 1
// Stage 1, minus the gates that need account state or a macro calendar
if na(fam)
evReject := true
evText := "regime_not_tradeable"
stState := 4
else if dist <= 0
evReject := true
evText := "stop_geometry"
stState := 4
else if oneSide and sideUsed
evReject := true
evText := "same_side_already_executed"
stState := 4
else if lastBarOfSession
evReject := true
evText := "session end"
stState := 4
else
cdLong := long
cdUp := stUp
cdFam := fam
cdStop := stop
cdExtreme := stExtreme
cdVwap := vwapV
cdSigma := sigma
cdPattern := pat
useBreak = trigMode == "all_breakout" or (trigMode == "pattern_breakout" and isBreakoutPattern(pat))
if useBreak
trig = long ? high + offTicks * tickSz : low - offTicks * tickSz
if execOkB(trig, stUp, vwapV, sigma)
cdLevel := trig
boBars := 0
stState := 3
evArm := true
evText := "arm " + str.tostring(trig, format.mintick)
evR := expectedRB(fam, long, trig, long ? trig - stop : stop - trig, vwapV, sigma)
else
evReject := true
evText := "breakout_level_unexecutable"
stState := 4
else
cdLevel := na
stState := 2
evArm := true
evText := "arm next open"
evR := expectedRB(fam, long, close, dist, vwapV, sigma)
// ═══ PLOTS ═══
cPurple = #6434E9
cBlue = #2C7CE5
cGreen = #49CC5C
cYellow = #F8C421
cOrange = #FB6640
cRed = #F82553
showB = showBands and inSess and not na(vwapV)
plot(showB ? vwapV : na, "VWAP", color=cPurple, linewidth=2)
pU1 = plot(showB ? lvl( noiseSig) : na, "+1 sigma", color=color.new(cYellow, 25))
pL1 = plot(showB ? lvl(-noiseSig) : na, "-1 sigma", color=color.new(cYellow, 25))
pU2 = plot(showB ? lvl( exLoSig) : na, "+2 sigma", color=cOrange)
pL2 = plot(showB ? lvl(-exLoSig) : na, "-2 sigma", color=cOrange)
pU3 = plot(showB ? lvl( exHiSig) : na, "+3 sigma", color=color.new(cRed, 10))
pL3 = plot(showB ? lvl(-exHiSig) : na, "-3 sigma", color=color.new(cRed, 10))
fill(pU1, pL1, color=color.new(cYellow, 93), title="Noise cloud")
fill(pU2, pU3, color=color.new(cOrange, 84), title="Upper extreme cloud")
fill(pL2, pL3, color=color.new(cOrange, 84), title="Lower extreme cloud")
bgcolor(showTint and inSess and regime == 1 ? color.new(cGreen, 94) : showTint and inSess and regime == -1 ? color.new(cRed, 95) : na, title="Trend regime")
// Resting stop-entry level while an order waits, then the live stop and target while in a trade
plot(stState == 3 and showLevels ? cdLevel : na, "Resting trigger", color=cBlue, style=plot.style_linebr, linewidth=2)
liveTgt = stState != 5 ? na : cdFam == 1 ? (cdLong ? lvl(2) : lvl(-2)) : posT1 ? vwapV : (cdLong ? lvl(-1) : lvl(1))
plot(stState == 5 and showLevels ? cdStop : na, "Live stop", color=cRed, style=plot.style_linebr, linewidth=2)
plot(stState == 5 and showLevels ? liveTgt : na, "Live target", color=cGreen, style=plot.style_linebr, linewidth=2)
plot(stState == 5 and showLevels ? posEntry: na, "Entry", color=color.new(cPurple, 40), style=plot.style_linebr)
plotshape(evArm and not cdLong, "Arm short", shape.triangledown, location.abovebar, cBlue, text="ARM", textcolor=cBlue, size=size.tiny)
plotshape(evArm and cdLong, "Arm long", shape.triangleup, location.belowbar, cBlue, text="ARM", textcolor=cBlue, size=size.tiny)
plotshape(evFire and not cdLong, "Fire short", shape.labeldown, location.abovebar, cRed, text="SHORT", textcolor=color.white, size=size.small)
plotshape(evFire and cdLong, "Fire long", shape.labelup, location.belowbar, cGreen, text="LONG", textcolor=color.white, size=size.small)
plotshape(evVoid, "Void", shape.xcross, location.abovebar, color.new(cOrange, 20), size=size.tiny)
plotshape(evReject, "Refused", shape.circle, location.belowbar, color.new(cRed, 35), size=size.tiny)
plotshape(evExit, "Exit", shape.square, location.abovebar, cPurple, size=size.tiny)
if evFire and showLevels
label.new(bar_index, cdStop, (cdFam == 1 ? "pullback " : "extension ") + (na(evR) ? "" : str.tostring(evR, "#.##") + "R ") + "stop " + str.tostring(cdStop, format.mintick), style=cdLong ? label.style_label_up : label.style_label_down, color=color.new(cRed, 88), textcolor=cRed, size=size.tiny)
if evExit and showLevels
label.new(bar_index, close, evText, style=label.style_label_left, color=color.new(cPurple, 85), textcolor=cPurple, size=size.tiny)
// ═══ STATUS TABLE ═══
stateName(s) => s == 0 ? "idle" : s == 1 ? "excursion" : s == 2 ? "entry gates" : s == 3 ? "breakout wait" : s == 5 ? "in position" : "cooldown"
regimeName(r) => na(r) ? "undefined" : r == 1 ? "uptrend" : r == -1 ? "downtrend" : r == 0 ? "balanced" : "mixed"
zoneName(z) => z == 3 ? "upper extension" : z == 2 ? "upper extreme" : z == 1 ? "upper transition" : z == 0 ? "noise" : z == -1 ? "lower transition" : z == -2 ? "lower extreme" : "lower extension"
var table tb = table.new(position.top_right, 2, 6, border_width=1)
if barstate.islast and showTable
bg = color.new(color.black, 76)
table.cell(tb, 0, 0, "STATE", text_color=color.gray, bgcolor=bg, text_size=size.tiny)
table.cell(tb, 1, 0, stateName(stState), text_color=cBlue, bgcolor=bg, text_size=size.tiny)
table.cell(tb, 0, 1, "TREND", text_color=color.gray, bgcolor=bg, text_size=size.tiny)
table.cell(tb, 1, 1, regimeName(regime), text_color=regime == 1 ? cGreen : regime == -1 ? cRed : color.gray, bgcolor=bg, text_size=size.tiny)
table.cell(tb, 0, 2, "ZONE", text_color=color.gray, bgcolor=bg, text_size=size.tiny)
table.cell(tb, 1, 2, na(vwapV) ? "-" : zoneName(zoneNow(close)), text_color=color.white, bgcolor=bg, text_size=size.tiny)
table.cell(tb, 0, 3, "SETUP", text_color=color.gray, bgcolor=bg, text_size=size.tiny)
table.cell(tb, 1, 3, stState == 0 or stState == 4 ? "-" : stState == 1 ? ((stUp ? "upper" : "lower") + " cloud, " + str.tostring(stBars) + " bars" + (stExtension ? ", extension" : "")) : ((cdFam == 1 ? "pullback " : "extension ") + (cdLong ? "long" : "short") + ", " + cdPattern), text_color=color.white, bgcolor=bg, text_size=size.tiny)
table.cell(tb, 0, 4, "TRIGGER", text_color=color.gray, bgcolor=bg, text_size=size.tiny)
table.cell(tb, 1, 4, stState == 3 ? (str.tostring(cdLevel, format.mintick) + " " + str.tostring(maxWait > 0 ? maxWait - boBars : 99) + " bars left") : stState == 2 ? "next open" : "-", text_color=cYellow, bgcolor=bg, text_size=size.tiny)
table.cell(tb, 0, 5, "STOP", text_color=color.gray, bgcolor=bg, text_size=size.tiny)
table.cell(tb, 1, 5, stState >= 2 ? (str.tostring(cdStop, format.mintick) + (posT1 and stState == 5 ? " (breakeven)" : "")) : "-", text_color=cRed, bgcolor=bg, text_size=size.tiny)
// ═══ ALERTS ═══
alertcondition(evArm, title="Setup armed", message="Stratomation VWAP Cloud: setup ARMED on {{ticker}}")
alertcondition(evFire, title="Entry fired", message="Stratomation VWAP Cloud: ENTRY on {{ticker}} at {{close}}")
alertcondition(evVoid, title="Setup voided", message="Stratomation VWAP Cloud: setup VOIDED on {{ticker}}")
alertcondition(evReject, title="Entry refused", message="Stratomation VWAP Cloud: entry REFUSED on {{ticker}}")
alertcondition(evExit, title="Trade exited", message="Stratomation VWAP Cloud: trade EXITED on {{ticker}}")
if evArm
alert("VWAP Cloud ARM " + syminfo.ticker + " " + (cdLong ? "long" : "short") + " " + evText + " (" + cdPattern + ")", alert.freq_once_per_bar_close)
if evFire
alert("VWAP Cloud ENTRY " + syminfo.ticker + " " + (cdLong ? "LONG" : "SHORT") + " " + evText + " stop " + str.tostring(cdStop, format.mintick) + " " + str.tostring(evR, "#.##") + "R", alert.freq_once_per_bar_close)
if evVoid or evReject
alert("VWAP Cloud " + (evVoid ? "VOID " : "REFUSED ") + syminfo.ticker + " " + evText, alert.freq_once_per_bar_close)
if evExit
alert("VWAP Cloud EXIT " + syminfo.ticker + " " + evText, alert.freq_once_per_bar_close)
Pinescript v5
— bytes