01 / Coil
A gold dot marks preparation. The base qualifies; its release level stops moving.
Daily breakouts · Pine Script v6 · Research 0.1
Let the base form.
Let price prove it.
A quiet chart. A level that stays put. Follow a tightening base into a confirmed breakout—and see when its exit rules call time.

One setup. One attempt.
Inspired by Kristjan Kullamägi, known as Qullamaggie: prior momentum, a tightening base, and rising moving averages. Vela adds a frozen breakout level and explicit rules for waiting, accepting, or rejecting the next move.
A gold dot marks preparation. The base qualifies; its release level stops moving.
A teal triangle marks a close above the level with enough volume and a controlled extension.
Follow the 10/20-day ribbon. A thin coral line holds the breakout day’s low.
A coral cross marks a later risk touch or close below your chosen average.
A tightening base qualifies at the close. The release level is sealed at 91.96725 for the next five bars.
The next close reaches 92 with 1.5× prior average volume. A single BREAK appears; the initial risk reference is 91.
The campaign stays active above its initial reference and the selected moving average. No repeated entry triangles.
A later close falls below SMA20. EXIT ends this observation; it does not open a short position.
Gold: SMA10 and frozen release. Teal: SMA20. Dashed coral: initial risk reference. The examples contain no live market data.
From source to chart
This is long-only. EXIT is not a short signal. The script places no orders; a risk-touch alert arrives at the daily close and cannot replace an intraday protective stop. Gaps and slippage can exceed your intended risk.
Trend + prior advanceClose > SMA10 > SMA20 > SMA50; SMA20 is higher than five bars earlier. The 40-bar advance ending before the 10-bar base must be at least 20%.
Contraction + proximityBase span ≤ 3 MTR. Last five bars’ mean true range ≤ 0.75× the pre-base 20-bar mean. Close within 1 MTR of SMA10 or SMA20. MTR is the simple 20-day mean true range, not Wilder ATR or percentage ADR.
A sealed release levelBase high + 0.10 MTR, frozen on the arming close. Valid for the next five closes. A low beneath the base or lost trend cancels it. A new coil needs the readiness condition to turn off and then on.
First attempt onlyFirst close above the level needs ≥ 1.5× prior 20-day average volume, close in the top 35% of its range, extension from SMA10 ≤ 2 frozen MTR, and positive close-to-low distance ≤ 1.5 frozen MTR. A failed attempt is consumed.
Two ways to finishFrom the bar after BREAK: low at or below the breakout-day low, or close below the chosen SMA10/20. Initial-risk touch takes precedence. No automatic partial sales or break-even adjustment.
Missing informationMissing volume blocks setups by default; disabling that filter changes the rules. Price exits continue without volume. Invalid price/history clears the campaign and shows UNAVAILABLE. Standard daily candles only.
Open source · No account required to download
Three moving averages, a restrained ribbon, and clear event markers. Candle coloring is optional and off by default. Use the compact panel to read the state without covering the chart.
Research release. 24 offline reference tests pass, including a complete synthetic lifecycle. Independent source review completed. Native TradingView compilation, chart replay and alert delivery remain unverified. No trading edge or improvement in returns has been measured.
//@version=6
// Konatus Vela 0.1.0 | Kutty Stauder | https://kuttystauder.com/konatus-vela/
// Inspired by Qullamaggie's published breakout framework; independent adaptation.
// Research indicator. No orders, position sizing or demonstrated trading edge.
// Standard daily candles. Events at close; no future data or intrabar entries.
indicator("KONATUS Vela · Breakout Trend", shorttitle="VELA", overlay=true, max_bars_back=500)
string setupGroup = "01 · Coil"
int baseLen = input.int(10, "Base length", minval=5, maxval=30, group=setupGroup)
int impulseLen = input.int(40, "Prior advance lookback", minval=20, maxval=120, group=setupGroup)
float minImpulse = input.float(20.0, "Minimum prior advance (%)", minval=0, maxval=200, step=5, group=setupGroup)
float contraction = input.float(0.75, "Range contraction ratio", minval=0.1, maxval=1, step=0.05, group=setupGroup)
int expiry = input.int(5, "Coil expires after bars", minval=1, maxval=20, group=setupGroup)
string releaseGroup = "02 · Release and ride"
bool requireVolume = input.bool(true, "Require volume expansion", group=releaseGroup)
float volMultiple = input.float(1.5, "Volume / prior 20-day average", minval=1, maxval=5, step=0.1, group=releaseGroup)
int trailLen = input.int(20, "Close-based trailing average", options=[10, 20], group=releaseGroup)
string styleGroup = "03 · Appearance"
bool showMAs = input.bool(true, "Show 10 / 20 / 50 averages", group=styleGroup)
bool showPanel = input.bool(true, "Show compact status", group=styleGroup)
bool paintBars = input.bool(false, "Color confirmed campaign candles", group=styleGroup)
color gold = input.color(#D7B777, "Coil / SMA10", group=styleGroup)
color teal = input.color(#72CCB1, "Breakout / SMA20", group=styleGroup)
color coral = input.color(#E58C7A, "Exit / initial risk", group=styleGroup)
color muted = #8D969A
// Rolling series are evaluated unconditionally. MTR uses simple, not Wilder smoothing.
bool supported = chart.is_standard and timeframe.isdaily and timeframe.multiplier == 1
bool priceOK = not na(open) and not na(high) and not na(low) and not na(close) and low > 0 and high >= math.max(open, close) and low <= math.min(open, close)
var int validRun = 0
validRun := priceOK ? validRun + 1 : 0
int warmup = math.max(55, math.max(baseLen + impulseLen + 1, baseLen + 21))
float ma10 = ta.sma(close, 10)
float ma20 = ta.sma(close, 20)
float ma50 = ta.sma(close, 50)
float trueRange = ta.tr(true)
float mtr = ta.sma(trueRange, 20)
float shortRange = ta.sma(trueRange, 5)
float oldRange = mtr[baseLen]
float top = ta.highest(high, baseLen)
float bottom = ta.lowest(low, baseLen)
float impulse = 100 * (close[baseLen] / close[baseLen + impulseLen] - 1)
float priorVolume = ta.sma(volume, 20)[1]
float volumeValid = ta.sma(not na(volume) and volume > 0 ? 1.0 : 0.0, 20)[1]
bool volumeData = not na(volume) and volume > 0 and volumeValid == 1 and priorVolume > 0
bool volumeOK = not requireVolume or (volumeData and volume >= volMultiple * priorVolume)
bool dataOK = supported and priceOK and validRun >= warmup and not na(mtr)
bool trend = close > ma10 and ma10 > ma20 and ma20 > ma50 and ma20 > ma20[5]
bool advance = impulse >= minImpulse
bool narrow = top - bottom <= 3 * mtr
bool contracted = shortRange <= contraction * oldRange
bool nearMA = math.min(math.abs(close - ma10), math.abs(close - ma20)) <= mtr
bool ready = dataOK and mtr > 0 and trend and advance and narrow and contracted and nearMA and (not requireVolume or volumeData)
string waiting = not trend ? "Wait for rising averages" : not advance ? "Need a prior advance" : not narrow ? "Base is too wide" : not contracted ? "Wait for contraction" : not nearMA ? "Price away from averages" : requireVolume and not volumeData ? "Volume unavailable" : "Wait for a fresh coil"
// 0 WAIT, 1 COIL, 2 RIDE. One attempt per fresh readiness edge.
var int state = 0
var int armedBar = na
var float level = na
var float floor = na
var float frozenRange = na
var float initialRisk = na
var bool previousReady = false
var string reason = "Load history"
var bool available = false
bool coilEvent = false
bool breakEvent = false
bool exitEvent = false
bool rejectEvent = false
if barstate.isconfirmed
available := dataOK
if not dataOK
state := 0
level := na
floor := na
frozenRange := na
initialRisk := na
previousReady := false
reason := not supported ? "Use standard 1D candles" : "Load valid price history"
else
if state == 2
float trail = trailLen == 10 ? ma10 : ma20
if low <= initialRisk or close < trail
reason := low <= initialRisk ? "Initial risk touched" : "Close below trail"
state := 0
exitEvent := true
level := na
initialRisk := na
else
reason := "Follow the moving average"
else if state == 1
int age = bar_index - armedBar
if age > expiry
state := 0
reason := "Coil expired"
else if low < floor or not trend
state := 0
reason := "Base or trend lost"
else if close > level
float location = high > low ? (close - low) / (high - low) : 0
bool quality = volumeOK and location >= 0.65 and close - ma10 <= 2 * frozenRange and close - low > 0 and close - low <= 1.5 * frozenRange
if quality
state := 2
breakEvent := true
initialRisk := low
reason := "Confirmed breakout"
else
state := 0
rejectEvent := true
reason := "Breakout filters failed"
else
reason := "Waiting for the release"
if state == 0
level := na
else if ready and not previousReady
state := 1
armedBar := bar_index
level := top + 0.10 * mtr
floor := bottom
frozenRange := mtr
initialRisk := na
coilEvent := true
reason := "Range sealed"
else
reason := waiting
previousReady := ready
// A live candle shows the previous completed averages, never a provisional event.
float confirmed10 = barstate.isconfirmed ? ma10 : ma10[1]
float confirmed20 = barstate.isconfirmed ? ma20 : ma20[1]
float confirmed50 = barstate.isconfirmed ? ma50 : ma50[1]
p10 = plot(supported and showMAs ? confirmed10 : na, "SMA10 · fast", color=color.new(gold, 15), linewidth=1)
p20 = plot(supported and showMAs ? confirmed20 : na, "SMA20 · trend", color=color.new(teal, 15), linewidth=2)
plot(supported and showMAs ? confirmed50 : na, "SMA50 · structure", color=color.new(muted, 45), linewidth=1)
fill(p10, p20, color=available and state > 0 ? color.new(state == 1 ? gold : teal, 93) : na, title="Campaign ribbon")
plot(available and (state == 1 or breakEvent) ? level : na, "Frozen release level", color=gold, linewidth=2, style=plot.style_linebr)
plot(available and state == 2 ? initialRisk : na, "Initial risk reference · not an order", color=color.new(coral, 20), linewidth=1, style=plot.style_linebr)
plotshape(coilEvent, title="New coil", style=shape.circle, location=location.belowbar, color=gold, size=size.tiny, text="C", textcolor=gold)
plotshape(breakEvent, title="Confirmed breakout", style=shape.triangleup, location=location.belowbar, color=teal, size=size.small, text="BREAK", textcolor=teal)
plotshape(exitEvent, title="Campaign exit", style=shape.xcross, location=location.abovebar, color=coral, size=size.tiny, text="EXIT", textcolor=coral)
barcolor(paintBars and barstate.isconfirmed and available ? exitEvent ? coral : state == 2 ? teal : state == 1 ? gold : na : na)
var table panel = table.new(position.bottom_right, 2, 3, bgcolor=color.new(#101D20, 7), frame_color=color.new(gold, 65), frame_width=1)
if barstate.islast
if showPanel
string label = not supported ? "1D ONLY" : not available ? "UNAVAILABLE" : breakEvent ? "BREAKOUT" : exitEvent ? "EXIT" : state == 2 ? "RIDE" : state == 1 ? "COIL" : "WAIT"
color accent = exitEvent ? coral : state == 2 ? teal : gold
table.cell(panel, 0, 0, "VELA", text_color=gold, text_size=size.small)
table.cell(panel, 1, 0, label, text_color=accent, text_size=size.small)
table.cell(panel, 0, 1, "At last close", text_color=muted, text_size=size.tiny)
table.cell(panel, 1, 1, not supported ? "Use standard 1D candles" : reason, text_color=color.white, text_size=size.tiny)
table.cell(panel, 0, 2, state == 2 ? "Initial risk" : "Release", text_color=muted, text_size=size.tiny)
table.cell(panel, 1, 2, state == 2 ? str.tostring(initialRisk, format.mintick) : state == 1 ? str.tostring(level, format.mintick) : "—", text_color=accent, text_size=size.small)
else
table.clear(panel, 0, 0, 1, 2)
alertcondition(coilEvent, "Vela · New coil", "VELA: new daily coil on {{ticker}}. Preparation, not an entry.")
alertcondition(breakEvent, "Vela · Breakout", "VELA: confirmed daily breakout on {{ticker}} at close {{close}}. Review risk before acting.")
alertcondition(exitEvent, "Vela · Exit", "VELA: initial risk touched or close below the trailing average on {{ticker}}. Daily-close observation, not an executed order.")
alertcondition(rejectEvent, "Vela · Rejected release", "VELA: first release failed quality filters on {{ticker}}. Wait for a fresh coil.")
The idea and its boundaries
Qullamaggie describes discretionary stock selection and often uses intraday opening-range entries, then partial profit-taking and moving-average exits. Vela’s daily-close confirmation, exact filters, five-bar expiry and frozen-level lifecycle are our own adaptation. They are not his complete system or a claim to his results.
Vela does not evaluate fundamentals, market breadth, news, liquidity or the visual quality of higher lows. Filters can delay good entries and still admit failed breakouts. Price history, sessions and data revisions can change readings. Not affiliated with or endorsed by Qullamaggie.