Appearance

Personalize your experience

Mode
Accent Color
Direction
All Docs/QuaScript//
QuaScript logo

QuaScript Documentation

Language guide, engine API, 601 builtins and the preset library · v3.0.0-phase1

Volume

Anchored VWAP Swings

analysis · presets/analysis/volume/anchored_vwap_swings.qs

Two VWAPs re-anchored automatically: one from the last confirmed swing high and one from the last swing low, each with a one-sigma band. The space between them is a gradient cloud, the anchors are tagged where they start and candles are colored by which side of both VWAPs price trades on.

QuaScriptengine result
Loading the engine result…
quatick-charts · QuaScript engine on synthetic 1-minute bars · drag to pan · scroll to zoom · Run executes the script on the engine (⌘/Ctrl+Enter while editing)
anchored_vwap_swings.qs
analysis "Anchored VWAP Swings" {
    overlay     = true
    max_objects = 200
}
/// Two VWAPs re-anchored automatically: one from the last confirmed swing high and one from the
/// last swing low, each with a one-sigma band. The space between them is a gradient cloud, the
/// anchors are tagged where they start and candles are colored by which side of both VWAPs price
/// trades on.
legs = param.int(10, title = "Swing legs", min = 2, max = 60)
band = param.float(1.0, title = "Band (sigma)", min = 0.2, max = 4, step = 0.1)

ph = ta.pivot_high(legs, legs)
pl = ta.pivot_low(legs, legs)
tp = bar.typical
v = math.max(volume, 1e-9)
persist hi_pv = na
persist hi_v = na
persist hi_pv2 = na
persist lo_pv = na
persist lo_v = na
persist lo_pv2 = na
a = ta.atr(14)
if not math.is_na(ph) {
    // re-anchor at the swing bar: sum the bars from the pivot up to now
    s_pv = 0.0
    s_v = 0.0
    s_pv2 = 0.0
    for k in 0..=legs {
        s_pv := s_pv + tp[k] * math.max(volume[k], 1e-9)
        s_v := s_v + math.max(volume[k], 1e-9)
        s_pv2 := s_pv2 + tp[k] * tp[k] * math.max(volume[k], 1e-9)
    }
    hi_pv := s_pv
    hi_v := s_v
    hi_pv2 := s_pv2
    draw.tag.create(bar.index - legs, ph + 0.3 * a, "AVWAP high", color = color.rose.600, shape = shape.tag_down, size = 0.8)
} else if not math.is_na(hi_v) {
    hi_pv := hi_pv + tp * v
    hi_v := hi_v + v
    hi_pv2 := hi_pv2 + tp * tp * v
}
if not math.is_na(pl) {
    s_pv = 0.0
    s_v = 0.0
    s_pv2 = 0.0
    for k in 0..=legs {
        s_pv := s_pv + tp[k] * math.max(volume[k], 1e-9)
        s_v := s_v + math.max(volume[k], 1e-9)
        s_pv2 := s_pv2 + tp[k] * tp[k] * math.max(volume[k], 1e-9)
    }
    lo_pv := s_pv
    lo_v := s_v
    lo_pv2 := s_pv2
    draw.tag.create(bar.index - legs, pl - 0.3 * a, "AVWAP low", color = color.emerald.600, shape = shape.tag_up, size = 0.8)
} else if not math.is_na(lo_v) {
    lo_pv := lo_pv + tp * v
    lo_v := lo_v + v
    lo_pv2 := lo_pv2 + tp * tp * v
}
vh = hi_pv / hi_v
vl = lo_pv / lo_v
sd_h = math.sqrt(math.max(hi_pv2 / hi_v - vh * vh, 0))
sd_l = math.sqrt(math.max(lo_pv2 / lo_v - vl * vl, 0))
p_h = render.line(vh, "VWAP from high", color = color.rose.400, width = 2)
p_l = render.line(vl, "VWAP from low", color = color.emerald.400, width = 2)
render.band(p_h, p_l, texture = "gradient", upper_color = color.rose.500, lower_color = color.emerald.500, opacity = 0.16, title = "Anchor cloud")
hu = render.line(vh + band * sd_h, "High band", color = color.translucent(color.rose.300, 60), width = 1, stroke = style.dotted)
hd = render.line(vh - band * sd_h, "High band low", color = color.translucent(color.rose.300, 60), width = 1, stroke = style.dotted)
lu = render.line(vl + band * sd_l, "Low band high", color = color.translucent(color.emerald.300, 60), width = 1, stroke = style.dotted)
ld = render.line(vl - band * sd_l, "Low band", color = color.translucent(color.emerald.300, 60), width = 1, stroke = style.dotted)
render.band(hu, hd, color = color.rose.500, opacity = 0.05, title = "High anchor band")
render.band(lu, ld, color = color.emerald.500, opacity = 0.05, title = "Low anchor band")
above = close > math.max(vh, vl)
below = close < math.min(vh, vl)
render.candle_color(above ? color.emerald.400 : below ? color.rose.400 : color.slate.500)
render.mark(above and not above[1], shape = shape.triangle_up, at = anchor.below_bar, color = color.emerald.300, title = "Above both")
render.mark(below and not below[1], shape = shape.triangle_down, at = anchor.above_bar, color = color.rose.300, title = "Below both")
alert.rule(above and not above[1], "Above both anchored VWAPs", "Close above the VWAPs from the last swing high and low")
alert.rule(below and not below[1], "Below both anchored VWAPs", "Close below the VWAPs from the last swing high and low")

Cumulative Volume Delta

analysis · presets/analysis/volume/cumulative_delta.qs · uses viz/palette

Buy / sell volume split by where each bar closes inside its range (or the host's real buy / sell volume when supplied), accumulated per session as candles, with a delta EMA and price / delta divergence marks.

QuaScriptengine result
Loading the engine result…
quatick-charts · QuaScript engine on synthetic 1-minute bars · drag to pan · scroll to zoom · Run executes the script on the engine (⌘/Ctrl+Enter while editing)
cumulative_delta.qs
analysis "Cumulative Volume Delta" {
    overlay = false
}
/// Buy / sell volume split by where each bar closes inside its range (or the host's real
/// buy / sell volume when supplied), accumulated per session as candles, with a delta EMA and
/// price / delta divergence marks.
use "viz/palette" as pal

len = param.int(20, title = "Signal length", min = 2)

range = high - low
buy_share = range > 0 ? (close - low) / range : 0.5
delta = volume * (2 * buy_share - 1)
new_day = time.day() != time.day(time[1])
persist cvd = 0.0
open_cvd = new_day ? 0.0 : cvd
cvd := open_cvd + delta
hi = math.max(open_cvd, cvd)
lo = math.min(open_cvd, cvd)
render.price.candles(o = open_cvd, h = hi, l = lo, c = cvd, up_color = pal.BULL, down_color = pal.BEAR, title = "CVD")
render.line(ta.ema(cvd, len), "CVD EMA", color = pal.ACCENT, width = 1)
render.level(0, "Zero", color = color.slate.600)
price_up = close > ta.highest(close[1], len)
cvd_weak = cvd < ta.highest(cvd[1], len)
price_dn = close < ta.lowest(close[1], len)
cvd_strong = cvd > ta.lowest(cvd[1], len)
render.mark(price_up and cvd_weak, shape = shape.x, at = anchor.top, color = pal.BEAR, text = "Absorption")
render.mark(price_dn and cvd_strong, shape = shape.x, at = anchor.bottom, color = pal.BULL, text = "Absorption")

Cumulative Delta Candles

analysis · presets/analysis/volume/delta_candles.qs

Cumulative volume delta drawn as candles: each bar opens at the previous total, its wicks reach the buy and sell volume and it closes at the new total. Buy / sell volume is the chart's own order flow when supplied, else bulk volume classification. An EMA ribbon of the delta and divergences against price show absorption.

QuaScriptengine result
Loading the engine result…
quatick-charts · QuaScript engine on synthetic 1-minute bars · drag to pan · scroll to zoom · Run executes the script on the engine (⌘/Ctrl+Enter while editing)
delta_candles.qs
analysis "Cumulative Delta Candles" {
    pane   = hpane
    height = 0.3
}
/// Cumulative volume delta drawn as candles: each bar opens at the previous total, its wicks
/// reach the buy and sell volume and it closes at the new total. Buy / sell volume is the chart's
/// own order flow when supplied, else bulk volume classification. An EMA ribbon of the delta and
/// divergences against price show absorption.
win   = param.int(20, title = "Classification window", min = 5, max = 200)
fast  = param.int(9, title = "Fast EMA", min = 2, max = 200)
slow  = param.int(34, title = "Slow EMA", min = 5, max = 500)
reset = param.bool(true, title = "Reset every day")

(buy, sell, imb) = hft.flow(win)
day = time.day()
new_day = day != day[1]
persist cvd = 0.0
prev = (reset and new_day) ? 0.0 : cvd
cvd := prev + buy - sell
c = cvd
h = math.max(prev + buy, math.max(prev, c))
l = math.min(prev - sell, math.min(prev, c))
render.price.candles(prev, h, l, c, up_color = color.teal.400, down_color = color.rose.500, title = "Delta candles")
ef = ta.ema(cvd, fast)
es = ta.ema(cvd, slow)
p_f = render.line(ef, "Fast EMA", color = ef >= es ? color.teal.300 : color.rose.300, width = 1.5)
p_s = render.line(es, "Slow EMA", color = color.slate.400, width = 1)
render.band(p_f, p_s, texture = "gradient", upper_color = color.teal.500, lower_color = color.rose.500, opacity = 0.2, title = "Delta ribbon")
(bull_div, bear_div) = ta.divergence(cvd, 5, 5, 60)
render.mark(bull_div, shape = shape.tag_up, at = anchor.bottom, color = color.teal.500, text = "Absorb")
render.mark(bear_div, shape = shape.tag_down, at = anchor.top, color = color.rose.500, text = "Absorb")
render.time_mark(reset and new_day, title = "Session reset", color = color.slate.600)
alert.rule(bull_div, "Bullish delta divergence", "Price made a lower low while delta made a higher low")
alert.rule(bear_div, "Bearish delta divergence", "Price made a higher high while delta made a lower high")

Money Flow Heat

analysis · presets/analysis/volume/money_flow_heat.qs · uses viz/palette

Money-flow index and Chaikin money flow on one scale (CMF rescaled to 0..100), filled between them, with an on-balance-volume trend tint in the background.

QuaScriptengine result
Loading the engine result…
quatick-charts · QuaScript engine on synthetic 1-minute bars · drag to pan · scroll to zoom · Run executes the script on the engine (⌘/Ctrl+Enter while editing)
money_flow_heat.qs
analysis "Money Flow Heat" {
    overlay = false
}
/// Money-flow index and Chaikin money flow on one scale (CMF rescaled to 0..100), filled between
/// them, with an on-balance-volume trend tint in the background.
use "viz/palette" as pal

len = param.int(14, title = "MFI length", min = 2)
cmf_len = param.int(20, title = "CMF length", min = 2)

mfi = ta.money_flow(len)
cmf = ta.chaikin_mf(cmf_len) * 50 + 50
obv = ta.obv()
pm = render.line(mfi, "MFI", color = pal.osc_heat(mfi), width = 2)
pc = render.line(cmf, "CMF (scaled)", color = color.violet.400, width = 1)
render.band(pm, pc, color = color.violet.500, opacity = 0.1)
render.level(80, "Distribution", color = color.rose.700, stroke = style.dashed)
render.level(20, "Accumulation", color = color.teal.700, stroke = style.dashed)
render.background(obv > ta.ema(obv, 50) ? color.translucent(pal.BULL, 94) : color.translucent(pal.BEAR, 94))

Untitled

analysis · presets/analysis/volume/obv.qs

On-Balance Volume (OBV)

QuaScriptengine result
Loading the engine result…
quatick-charts · QuaScript engine on synthetic 1-minute bars · drag to pan · scroll to zoom · Run executes the script on the engine (⌘/Ctrl+Enter while editing)
obv.qs
analysis "Untitled" {
    render  = canvas
    overlay = false
}
// On-Balance Volume (OBV)
val = ta.obv()
render.line(val, "OBV", color=color.cyan.500, width=2)

Auction Profile Suite

analysis · presets/analysis/volume/profile_suite.qs

A complete auction view: the visible-range volume profile on the chart, the developing session point of control and value area as step lines with a cloud between the value-area edges, and two vertical panes beside the price axis (time at price and the buy / sell split). Candles outside value are highlighted.

QuaScriptengine result
Loading the engine result…
quatick-charts · QuaScript engine on synthetic 1-minute bars · drag to pan · scroll to zoom · Run executes the script on the engine (⌘/Ctrl+Enter while editing)
profile_suite.qs
analysis "Auction Profile Suite" {
    overlay = true
    width   = 130
}
/// A complete auction view: the visible-range volume profile on the chart, the developing session
/// point of control and value area as step lines with a cloud between the value-area edges, and
/// two vertical panes beside the price axis (time at price and the buy / sell split). Candles
/// outside value are highlighted.
rows = param.int(48, title = "Rows", min = 12, max = 200)
va   = param.float(0.7, title = "Value area", min = 0.5, max = 0.95, step = 0.05)

(poc, vah, val) = ta.volume_profile(rows, va, "session")
render.profile.volume(rows = 32, value_area = 0.7, width = 160, position = "right", period = "visible", title = "Visible profile")
p_vah = render.step(vah, "Value high", color = color.sky.400, width = 1.5)
p_val = render.step(val, "Value low", color = color.sky.400, width = 1.5)
render.band(p_vah, p_val, texture = "cloud", color = color.sky.500, opacity = 0.1, title = "Value area")
render.step(poc, "Session POC", color = color.amber.400, width = 2.5)
render.vpane.profile(kind = "tpo", title = "Time at price", rows = 60)
render.vpane.profile(kind = "buy_sell", title = "Buy | Sell", rows = 60)
above = close > vah
below = close < val
render.candle_color(above ? color.sky.300 : below ? color.orange.400 : na)
render.mark(ta.cross_up(close, vah), shape = shape.triangle_up, at = anchor.below_bar, color = color.sky.300, title = "Acceptance above value")
render.mark(ta.cross_down(close, val), shape = shape.triangle_down, at = anchor.above_bar, color = color.orange.400, title = "Acceptance below value")
alert.rule(ta.cross_up(close, vah), "Breakout above value", "Close above the session value-area high")
alert.rule(ta.cross_down(close, val), "Breakdown below value", "Close below the session value-area low")

Relative Volume

analysis · presets/analysis/volume/relative_volume.qs

Volume divided by its average for the same time of day over the last N sessions (time-of-day RVOL), computed with a map keyed by the minute of the day. Bars above the threshold glow.

QuaScriptengine result
Loading the engine result…
quatick-charts · QuaScript engine on synthetic 1-minute bars · drag to pan · scroll to zoom · Run executes the script on the engine (⌘/Ctrl+Enter while editing)
relative_volume.qs
analysis "Relative Volume" {
    overlay = false
}
/// Volume divided by its average for the same time of day over the last N sessions (time-of-day
/// RVOL), computed with a map keyed by the minute of the day. Bars above the threshold glow.
days  = param.int(10, title = "Sessions", min = 1, max = 60)
thr   = param.float(2.0, title = "Highlight above", min = 1, max = 10, step = 0.1)

persist avgs = map.create(float, float)
persist seen = map.create(float, float)
slot = math.floor(time.of_day() / 60)
avg = map.get(avgs, slot)
rvol = math.is_na(avg) or avg <= 0 ? na : volume / avg
// running mean of this minute-of-day slot over (at most) the last `days` sessions
n = math.min(map.get(seen, slot, 0) + 1, days)
map.set(avgs, slot, math.is_na(avg) ? volume : avg + (volume - avg) / n)
map.set(seen, slot, n)
render.histogram(rvol, "RVOL", color = rvol >= thr ? color.amber.400 : rvol >= 1 ? color.teal.600 : color.slate.600)
render.level(1, "Average", color = color.slate.500, stroke = style.dotted)
render.level(thr, "Threshold", color = color.amber.700, stroke = style.dashed)
alert.rule(ta.cross_up(rvol, thr), "Unusual volume", "Volume is well above its usual level for this time of day")

Session Levels & Opening Range

analysis · presets/analysis/volume/session_levels.qs

Key intraday reference levels: the previous day's high, low and close, today's open, and the opening range of the first N bars drawn as a zone with breakout targets at one and two range heights. Levels start at the session and end with it; breakouts are tagged.

QuaScriptengine result
Loading the engine result…
quatick-charts · QuaScript engine on synthetic 1-minute bars · drag to pan · scroll to zoom · Run executes the script on the engine (⌘/Ctrl+Enter while editing)
session_levels.qs
analysis "Session Levels & Opening Range" {
    overlay     = true
    max_objects = 400
}
/// Key intraday reference levels: the previous day's high, low and close, today's open, and the
/// opening range of the first N bars drawn as a zone with breakout targets at one and two range
/// heights. Levels start at the session and end with it; breakouts are tagged.
or_bars = param.int(6, title = "Opening range bars", min = 1, max = 60)

day = time.day()
new_day = day != day[1]
persist d_hi = na
persist d_lo = na
persist p_hi = na
persist p_lo = na
persist p_cl = na
persist d_open = na
persist n = 0
persist or_hi = na
persist or_lo = na
persist or_id = 0.0
persist broke = 0
if new_day {
    p_hi := d_hi
    p_lo := d_lo
    p_cl := close[1]
    d_hi := high
    d_lo := low
    d_open := open
    n := 1
    or_hi := high
    or_lo := low
    broke := 0
    or_id := 0
} else {
    d_hi := math.max(d_hi, high)
    d_lo := math.min(d_lo, low)
    n := n + 1
    if n <= or_bars {
        or_hi := math.max(or_hi, high)
        or_lo := math.min(or_lo, low)
    }
}
or_done = n == or_bars
if or_done {
    or_id := draw.zone.create(bar.index - or_bars + 1, or_hi, bar.index, or_lo, color = color.translucent(color.amber.400, 85), border_color = color.amber.400, text = "Opening range")
}
if n > or_bars and or_id > 0 { draw.zone.update(or_id, right = bar.index) }
h = or_hi - or_lo
a = ta.atr(14)
up_break = n > or_bars and broke == 0 and close > or_hi
dn_break = n > or_bars and broke == 0 and close < or_lo
if up_break {
    broke := 1
    draw.tag.create(bar.index, low - 0.3 * a, "OR breakout", color = color.emerald.600, shape = shape.tag_up)
}
if dn_break {
    broke := -1
    draw.tag.create(bar.index, high + 0.3 * a, "OR breakdown", color = color.rose.600, shape = shape.tag_down)
}
render.step(p_hi, "Previous high", color = color.rose.400, width = 1.5, stroke = style.dashed)
render.step(p_lo, "Previous low", color = color.emerald.400, width = 1.5, stroke = style.dashed)
render.step(p_cl, "Previous close", color = color.slate.300, width = 1, stroke = style.dotted)
render.step(d_open, "Today's open", color = color.sky.300, width = 1.5)
render.step(n > or_bars and broke >= 0 ? or_hi + h : na, "Target +1R", color = color.translucent(color.emerald.300, 40), width = 1)
render.step(n > or_bars and broke >= 0 ? or_hi + 2 * h : na, "Target +2R", color = color.translucent(color.emerald.300, 60), width = 1)
render.step(n > or_bars and broke <= 0 ? or_lo - h : na, "Target -1R", color = color.translucent(color.rose.300, 40), width = 1)
render.step(n > or_bars and broke <= 0 ? or_lo - 2 * h : na, "Target -2R", color = color.translucent(color.rose.300, 60), width = 1)
render.time_mark(new_day, title = "Session start", color = color.slate.700)
alert.rule(up_break, "Opening range breakout", "Close above the opening range")
alert.rule(dn_break, "Opening range breakdown", "Close below the opening range")

Session Volume Profile

analysis · presets/analysis/volume/session_volume_profile.qs

Per-session volume profile drawn on the GPU, plus the developing session POC / value area as lines so they can be traded against.

QuaScriptengine result
Loading the engine result…
quatick-charts · QuaScript engine on synthetic 1-minute bars · drag to pan · scroll to zoom · Run executes the script on the engine (⌘/Ctrl+Enter while editing)
session_volume_profile.qs
analysis "Session Volume Profile" {
    overlay = true
    render  = webgl
}
// Per-session volume profile drawn on the GPU, plus the developing session POC / value area
// as lines so they can be traded against.
rows  = param.int(80, title = "Price rows", min = 8, max = 400)
area  = param.float(0.70, title = "Value area", min = 0.5, max = 0.95, step = 0.05)
width = param.float(180, title = "Width (px)", min = 40, max = 500)

render.profile.session(rows = rows, value_area = area, width = width, up_color = color.emerald.500, down_color = color.rose.500, poc_color = color.amber.400, title = "Session profile")
(poc, vah, val) = ta.volume_profile(rows = 24, value_area = 0.7, period = "session")
render.step(poc, "Developing POC", color = color.amber.500, width = 2)
p_vah = render.step(vah, "Value area high", color = color.rose.400, width = 1)
p_val = render.step(val, "Value area low", color = color.emerald.400, width = 1)
render.band(p_vah, p_val, color = color.blue.500, opacity = 0.06, title = "Value area")
render.mark(ta.cross_up(close, vah), shape = shape.triangle_up, at = anchor.below_bar, color = color.cyan.500, text = "VAH break")
render.mark(ta.cross_down(close, val), shape = shape.triangle_down, at = anchor.above_bar, color = color.rose.500, text = "VAL break")
alert.rule(ta.cross_up(close, vah), "Value area breakout", "Price broke above the value area high")
alert.rule(ta.cross_down(close, val), "Value area breakdown", "Price broke below the value area low")

Smart Money Flow Cloud

analysis · presets/analysis/volume/smart_money_flow.qs

Chaikin money flow, the slope of on-balance volume and the order-flow imbalance combined into one flow score (-1 .. +1). The score paints a volatility cloud around a volume-weighted average, widens it while flow is strong and colors the candles; marks show flow flipping against price (distribution in a rally, accumulation in a sell-off).

QuaScriptengine result
Loading the engine result…
quatick-charts · QuaScript engine on synthetic 1-minute bars · drag to pan · scroll to zoom · Run executes the script on the engine (⌘/Ctrl+Enter while editing)
smart_money_flow.qs
analysis "Smart Money Flow Cloud" {
    overlay = true
}
/// Chaikin money flow, the slope of on-balance volume and the order-flow imbalance combined into
/// one flow score (-1 .. +1). The score paints a volatility cloud around a volume-weighted
/// average, widens it while flow is strong and colors the candles; marks show flow flipping
/// against price (distribution in a rally, accumulation in a sell-off).
len  = param.int(21, title = "Flow length", min = 5, max = 200)
blen = param.int(34, title = "Basis length", min = 5, max = 300)

cmf = ta.chaikin_mf(len)
obv = ta.obv()
obv_s = math.clamp((obv - obv[len]) / math.max(stat.sum(volume, len), 1e-12) * 2, -1, 1)
(buy, sell, imb) = hft.flow(len)
flow = ta.ema(math.clamp(cmf * 3, -1, 1) * 0.4 + obv_s * 0.3 + imb * 0.3, 5)
basis = ta.vwma(close, blen)
a = ta.atr(blen)
w = a * (1 + math.abs(flow) * 1.5)
heat = flow >= 0 ? color.gradient(flow, 0, 0.6, color.slate.400, color.teal.300) : color.gradient(flow, -0.6, 0, color.pink.400, color.slate.400)
pb = render.line(basis, "Flow basis", color = heat, width = 3)
pu = render.line(basis + w, "Cloud top", color = color.translucent(color.teal.300, 70), width = 1)
pl = render.line(basis - w, "Cloud bottom", color = color.translucent(color.pink.300, 70), width = 1)
render.band(pu, pb, texture = "gradient", upper_color = color.teal.400, lower_color = color.slate.800, opacity = 0.25, title = "Buy pressure")
render.band(pb, pl, texture = "gradient", upper_color = color.slate.800, lower_color = color.pink.500, opacity = 0.25, title = "Sell pressure")
render.candle_color(color.gradient(flow, -0.6, 0.6, color.pink.500, color.teal.400))
distribution = flow < -0.15 and close > basis and ta.rising(close, 3)
accumulation = flow > 0.15 and close < basis and ta.falling(close, 3)
render.mark(distribution and not distribution[1], shape = shape.tag_down, at = anchor.above_bar, color = color.pink.500, text = "Dist")
render.mark(accumulation and not accumulation[1], shape = shape.tag_up, at = anchor.below_bar, color = color.teal.500, text = "Accum")
alert.rule(distribution and not distribution[1], "Distribution", "Money flows out while price rises")
alert.rule(accumulation and not accumulation[1], "Accumulation", "Money flows in while price falls")

Value Area Migration

analysis · presets/analysis/volume/value_area_migration.qs

The developing session value area (POC, VAH, VAL) and the previous session's value area carried forward as a zone. How today's value relates to yesterday's (higher, lower, inside, outside or overlapping) classifies the auction; the class is shown in a table and colors the candles, and acceptance outside the prior value is marked.

QuaScriptengine result
Loading the engine result…
quatick-charts · QuaScript engine on synthetic 1-minute bars · drag to pan · scroll to zoom · Run executes the script on the engine (⌘/Ctrl+Enter while editing)
value_area_migration.qs
analysis "Value Area Migration" {
    overlay     = true
    max_objects = 300
}
/// The developing session value area (POC, VAH, VAL) and the previous session's value area
/// carried forward as a zone. How today's value relates to yesterday's (higher, lower, inside,
/// outside or overlapping) classifies the auction; the class is shown in a table and colors the
/// candles, and acceptance outside the prior value is marked.
rows = param.int(48, title = "Rows", min = 12, max = 200)
va   = param.float(0.7, title = "Value area", min = 0.5, max = 0.95, step = 0.05)

(poc, vah, val) = ta.volume_profile(rows, va, "session")
day = time.day()
new_day = day != day[1]
persist p_vah = na
persist p_val = na
persist p_poc = na
persist zid = 0.0
if new_day {
    p_vah := vah[1]
    p_val := val[1]
    p_poc := poc[1]
    if not math.is_na(p_vah) {
        zid := draw.zone.create(bar.index, p_vah, bar.index, p_val, color = color.translucent(color.slate.400, 88), border_color = color.slate.500, text = "Prior value")
    }
} else if zid > 0 {
    draw.zone.update(zid, right = bar.index)
}
higher = val > p_vah
lower = vah < p_val
inside = vah <= p_vah and val >= p_val
outside = vah > p_vah and val < p_val
cls = higher ? 2 : lower ? -2 : inside ? 0 : outside ? 3 : (poc > p_poc ? 1 : -1)
tone = match cls {
    case 2 { color.emerald.400 }
    case 1 { color.teal.500 }
    case -1 { color.orange.500 }
    case -2 { color.rose.400 }
    case 3 { color.violet.400 }
    else { color.slate.400 }
}
pu = render.step(vah, "Value high", color = color.sky.400, width = 1.5)
pl = render.step(val, "Value low", color = color.sky.400, width = 1.5)
render.band(pu, pl, texture = "gradient", upper_color = color.sky.500, lower_color = color.indigo.500, opacity = 0.12, title = "Developing value")
render.step(poc, "Developing POC", color = color.amber.400, width = 2.5)
render.step(p_poc, "Prior POC", color = color.translucent(color.amber.300, 50), width = 1, stroke = style.dashed)
render.candle_color(tone)
acc_up = close > p_vah and close[1] > p_vah and close[2] <= p_vah
acc_dn = close < p_val and close[1] < p_val and close[2] >= p_val
render.mark(acc_up, shape = shape.tag_up, at = anchor.below_bar, color = color.emerald.500, text = "Accepted")
render.mark(acc_dn, shape = shape.tag_down, at = anchor.above_bar, color = color.rose.500, text = "Accepted")
persist t = draw.table.create(anchor.top_right, 2, 2)
if bar.is_last {
    name = match cls {
        case 2 { "Higher value" }
        case 1 { "Overlap, higher" }
        case -1 { "Overlap, lower" }
        case -2 { "Lower value" }
        case 3 { "Outside day" }
        else { "Inside day" }
    }
    draw.table.cell(t, 0, 0, "Auction", text_color = color.slate.400)
    draw.table.cell(t, 0, 1, name, bg_color = tone, text_color = color.readable(tone))
    draw.table.cell(t, 1, 0, "POC shift", text_color = color.slate.400)
    draw.table.cell(t, 1, 1, str.format("{:+.2f}", poc - p_poc))
}
alert.rule(acc_up, "Accepted above prior value", "Two closes above yesterday's value-area high")
alert.rule(acc_dn, "Accepted below prior value", "Two closes below yesterday's value-area low")

Volume Heat Candles

analysis · presets/analysis/volume/volume_heat_candles.qs

Candles colored by relative volume (cold = thin, white-hot = climactic) with a volume-weighted average cloud and markers for climax bars, stopping volume (heavy volume with a small body at an extreme) and no-demand / no-supply bars.

QuaScriptengine result
Loading the engine result…
quatick-charts · QuaScript engine on synthetic 1-minute bars · drag to pan · scroll to zoom · Run executes the script on the engine (⌘/Ctrl+Enter while editing)
volume_heat_candles.qs
analysis "Volume Heat Candles" {
    overlay = true
}
/// Candles colored by relative volume (cold = thin, white-hot = climactic) with a volume-weighted
/// average cloud and markers for climax bars, stopping volume (heavy volume with a small body at
/// an extreme) and no-demand / no-supply bars.
len   = param.int(50, title = "Volume average", min = 5, max = 500)
climax = param.float(2.5, title = "Climax multiple", min = 1.5, max = 10, step = 0.1)

rv = volume / math.max(ta.sma(volume, len), 1e-12)
heat = rv < 1 ? color.gradient(rv, 0, 1, color.slate.700, color.sky.500) : rv < 2 ? color.gradient(rv, 1, 2, color.sky.500, color.amber.400) : color.gradient(rv, 2, climax, color.amber.400, color.yellow.100)
render.candle_color(heat)
vw = ta.vwma(close, 20)
em = ta.ema(close, 20)
p_v = render.line(vw, "VWMA", color = color.amber.300, width = 2)
p_e = render.line(em, "EMA", color = color.sky.300, width = 1)
render.band(p_v, p_e, texture = "gradient", upper_color = color.amber.500, lower_color = color.sky.500, opacity = 0.2, title = "Participation cloud")
body = math.abs(close - open) / math.max(high - low, 1e-12)
hi_ext = high >= ta.highest(high, 20)
lo_ext = low <= ta.lowest(low, 20)
is_climax = rv >= climax
stopping = rv > 2 and body < 0.3 and (hi_ext or lo_ext)
no_demand = close > open and rv < 0.5 and high > high[1]
no_supply = close < open and rv < 0.5 and low < low[1]
render.mark(is_climax and close > open, shape = shape.star, at = anchor.above_bar, color = color.yellow.200, title = "Buying climax")
render.mark(is_climax and close < open, shape = shape.star, at = anchor.below_bar, color = color.yellow.200, title = "Selling climax")
render.mark(stopping and lo_ext, shape = shape.square, at = anchor.below_bar, color = color.emerald.400, size = 0.7, title = "Stopping volume")
render.mark(stopping and hi_ext, shape = shape.square, at = anchor.above_bar, color = color.rose.400, size = 0.7, title = "Stopping volume high")
render.mark(no_demand, shape = shape.x, at = anchor.above_bar, color = color.slate.400, size = 0.5, title = "No demand")
render.mark(no_supply, shape = shape.x, at = anchor.below_bar, color = color.slate.400, size = 0.5, title = "No supply")
alert.rule(is_climax, "Volume climax", "Volume above the climax multiple of its average")

Volume Oscillator

analysis · presets/analysis/volume/volume_osc.qs

Percent difference between a short and a long volume EMA.

QuaScriptengine result
Loading the engine result…
quatick-charts · QuaScript engine on synthetic 1-minute bars · drag to pan · scroll to zoom · Run executes the script on the engine (⌘/Ctrl+Enter while editing)
volume_osc.qs
analysis "Volume Oscillator" {
    overlay = false
}
// Percent difference between a short and a long volume EMA.
short_len = param.int(5, title = "Short", min = 1, max = 50)
long_len  = param.int(10, title = "Long", min = 1, max = 100)
osc = ta.volume_osc(short_len, long_len)
render.histogram(osc, "Volume oscillator", color = osc >= 0 ? color.teal.500 : color.rose.500)
render.level(0, "Zero", color = color.slate.500)

Volume Profile

analysis · presets/analysis/volume/volume_profile.qs

Visible-range volume profile with POC and value area, computed by the engine.

QuaScriptengine result
Loading the engine result…
quatick-charts · QuaScript engine on synthetic 1-minute bars · drag to pan · scroll to zoom · Run executes the script on the engine (⌘/Ctrl+Enter while editing)
volume_profile.qs
analysis "Volume Profile" {
    overlay = true
    render  = webgl
}
// Visible-range volume profile with POC and value area, computed by the engine.
rows  = param.int(70, title = "Price rows", min = 8, max = 400)
area  = param.float(0.70, title = "Value area", min = 0.5, max = 0.95, step = 0.05)
width = param.float(160, title = "Width (px)", min = 40, max = 400)
render.profile.volume(rows = rows, value_area = area, width = width, up_color = color.teal.500, down_color = color.rose.500, poc_color = color.amber.400)

VWAP Constellation

analysis · presets/analysis/volume/vwap_constellation.qs

Daily and weekly VWAP with three deviation bands each. The daily bands are cloud-textured, the VWAP line is colored by its slope, the weekly VWAP acts as the anchor and marks show rejections at the outer bands.

QuaScriptengine result
Loading the engine result…
quatick-charts · QuaScript engine on synthetic 1-minute bars · drag to pan · scroll to zoom · Run executes the script on the engine (⌘/Ctrl+Enter while editing)
vwap_constellation.qs
analysis "VWAP Constellation" {
    overlay = true
}
/// Daily and weekly VWAP with three deviation bands each. The daily bands are cloud-textured,
/// the VWAP line is colored by its slope, the weekly VWAP acts as the anchor and marks show
/// rejections at the outer bands.
mult1 = param.float(1.0, title = "Band 1", min = 0.1, max = 5, step = 0.1)
mult2 = param.float(2.0, title = "Band 2", min = 0.1, max = 5, step = 0.1)
mult3 = param.float(3.0, title = "Band 3", min = 0.1, max = 6, step = 0.1)
show_week = param.bool(true, title = "Weekly VWAP")

(v1, u1, l1) = ta.vwap(bar.typical, "day", mult1)
(v2, u2, l2) = ta.vwap(bar.typical, "day", mult2)
(v3, u3, l3) = ta.vwap(bar.typical, "day", mult3)
(w, wu, wl) = ta.vwap(bar.typical, "week", 1.0)
slope = v1 - v1[1]
pv = render.line(v1, "Daily VWAP", color = slope >= 0 ? color.amber.300 : color.orange.500, width = 3)
pu1 = render.line(u1, "+1", color = color.translucent(color.sky.300, 50), width = 1)
pl1 = render.line(l1, "-1", color = color.translucent(color.sky.300, 50), width = 1)
pu2 = render.line(u2, "+2", color = color.translucent(color.indigo.300, 45), width = 1)
pl2 = render.line(l2, "-2", color = color.translucent(color.indigo.300, 45), width = 1)
pu3 = render.line(u3, "+3", color = color.translucent(color.fuchsia.400, 30), width = 1.5)
pl3 = render.line(l3, "-3", color = color.translucent(color.fuchsia.400, 30), width = 1.5)
render.band(pu1, pl1, texture = "cloud", color = color.sky.500, opacity = 0.12, title = "Core value")
render.band(pu2, pu1, texture = "gradient", upper_color = color.indigo.500, lower_color = color.sky.500, opacity = 0.14, title = "Upper extension")
render.band(pl1, pl2, texture = "gradient", upper_color = color.sky.500, lower_color = color.indigo.500, opacity = 0.14, title = "Lower extension")
render.band(pu3, pu2, color = color.fuchsia.500, opacity = 0.08, title = "Upper extreme")
render.band(pl2, pl3, color = color.fuchsia.500, opacity = 0.08, title = "Lower extreme")
render.line(show_week ? w : na, "Weekly VWAP", color = color.lime.300, width = 2, stroke = style.dashed)
rej_hi = high > u2 and close < u2
rej_lo = low < l2 and close > l2
render.mark(rej_hi, shape = shape.triangle_down, at = anchor.above_bar, color = color.fuchsia.400, title = "Upper rejection")
render.mark(rej_lo, shape = shape.triangle_up, at = anchor.below_bar, color = color.sky.300, title = "Lower rejection")
render.candle_color(close > u2 ? color.fuchsia.400 : close < l2 ? color.sky.400 : na)
alert.rule(rej_hi, "Upper band rejection", "Wick above the +2 band closed back inside")
alert.rule(rej_lo, "Lower band rejection", "Wick below the -2 band closed back inside")

VWAP Deviation Bands

analysis · presets/analysis/volume/vwap_deviation_bands.qs

Session-anchored VWAP with 1, 2 and 3 standard-deviation bands (volume weighted), a stepped previous-session VWAP and band-touch marks.

QuaScriptengine result
Loading the engine result…
quatick-charts · QuaScript engine on synthetic 1-minute bars · drag to pan · scroll to zoom · Run executes the script on the engine (⌘/Ctrl+Enter while editing)
vwap_deviation_bands.qs
analysis "VWAP Deviation Bands" {
    overlay = true
}
/// Session-anchored VWAP with 1, 2 and 3 standard-deviation bands (volume weighted), a
/// stepped previous-session VWAP and band-touch marks.
period = param.choice("day", title = "Anchor", options = ["day", "week", "month"])

(vw, u1, l1) = ta.vwap(bar.typical, period, 1)
(_, u2, l2) = ta.vwap(bar.typical, period, 2)
(_, u3, l3) = ta.vwap(bar.typical, period, 3)
render.line(vw, "VWAP", color = color.pink.400, width = 2)
a1 = render.line(u1, "+1 sd", color = color.slate.500, width = 1)
b1 = render.line(l1, "-1 sd", color = color.slate.500, width = 1)
a2 = render.line(u2, "+2 sd", color = color.amber.600, width = 1)
b2 = render.line(l2, "-2 sd", color = color.amber.600, width = 1)
a3 = render.line(u3, "+3 sd", color = color.rose.600, width = 1, stroke = style.dashed)
b3 = render.line(l3, "-3 sd", color = color.rose.600, width = 1, stroke = style.dashed)
render.band(a1, b1, color = color.pink.500, opacity = 0.06)
render.band(a3, a2, color = color.rose.500, opacity = 0.06)
render.band(b2, b3, color = color.teal.500, opacity = 0.06)
render.mark(ta.cross_up(high, u2), shape = shape.circle, at = anchor.above_bar, color = color.amber.500, size = 0.6)
render.mark(ta.cross_down(low, l2), shape = shape.circle, at = anchor.below_bar, color = color.amber.500, size = 0.6)
alert.rule(ta.cross_up(high, u3), "VWAP +3 sd", "Price reached the third upper band")
alert.rule(ta.cross_down(low, l3), "VWAP -3 sd", "Price reached the third lower band")

VWAP Bands

analysis · presets/analysis/volume/vwap.qs

Session-anchored VWAP with volume-weighted deviation bands.

QuaScriptengine result
Loading the engine result…
quatick-charts · QuaScript engine on synthetic 1-minute bars · drag to pan · scroll to zoom · Run executes the script on the engine (⌘/Ctrl+Enter while editing)
vwap.qs
analysis "VWAP Bands" {
    overlay = true
}
// Session-anchored VWAP with volume-weighted deviation bands.
mult = param.float(1.5, title = "Band multiple", min = 0.1, max = 5, step = 0.1)
(vwap, upper, lower) = ta.vwap(mult = mult)
render.line(vwap, "VWAP", color = color.pink.500, width = 2)
p_up = render.line(upper, "Upper band", color = color.blue.500, width = 1)
p_lo = render.line(lower, "Lower band", color = color.blue.500, width = 1)
render.band(p_up, p_lo, color = color.pink.500, opacity = 0.08, title = "VWAP envelope")
render.mark(ta.cross_up(close, vwap), shape = shape.arrow_up, at = anchor.below_bar, color = color.emerald.500, text = "Above")
render.mark(ta.cross_down(close, vwap), shape = shape.arrow_down, at = anchor.above_bar, color = color.rose.500, text = "Below")
alert.rule(ta.cross_up(close, vwap), "VWAP bullish cross", "Price crossed above VWAP")
alert.rule(ta.cross_down(close, vwap), "VWAP bearish cross", "Price crossed below VWAP")

© 2026 Quatick Technologies. All rights reserved.