Control flow decides what runs on each bar. kScript gives you the familiar C-style toolkit: if/else branches, for loops, the ? : ternary, and switch. Everything here runs inside the per-bar calculation pass, so the code executes once per bar as the engine walks the series.
if / else
Branch on any boolean condition. The else is optional.
//@version=2
define(title="Bar direction", position="offchart", axis=true)
timeseries trade = ohlcv(symbol=currentSymbol, exchange=currentExchange)
var direction = 0
if (trade.close > trade.open) {
direction = 1 // up bar
} else if (trade.close < trade.open) {
direction = -1 // down bar
} else {
direction = 0 // doji
}
plotLine(value=direction, colors=["#2563eb"], width=2, label=["Direction"], desc=["+1 up, -1 down, 0 flat"])Ternary expressions
For a single inline choice, the condition ? a : b ternary is cleaner than a full if. It's the idiomatic way to pick a value or suppress a plot:
// Pick a price based on trend
var anchor = trade.close > trade.open ? trade.high : trade.low
// Plot a marker only on signal bars; na draws nothing
var signal = crossover(seriesA=fast, seriesB=slow) ? trade.low : nafor loops
Use a C-style for when you need a fixed number of iterations: summing a window, scanning buckets, accumulating a value. Increment with n += 1.
// Sum the last 5 closes by hand
var total = 0
for (var n = 0; n < 5; n += 1) {
total += trade.close[n]
}switch
switch matches a value against case labels with a default fallback. Use it to map a discrete code to a value or branch.
var mode = barIndex % 3
var picked = trade.close
switch (mode) {
case 0:
picked = trade.close
case 1:
picked = trade.high
default:
picked = trade.low
}Everything together
This script exercises a for loop, an if/else branch, a ternary, and a switch, then folds all four results into one plotted value.
//@version=2
define(title="Control Flow Happy Path", position="offchart", axis=true)
timeseries trade = ohlcv(symbol=currentSymbol, exchange=currentExchange)
var loopTotal = 0
for (var n = 0; n < 5; n += 1) {
loopTotal += n
}
var branch = 0
if (trade.close > trade.open) {
branch = 1
} else {
branch = -1
}
var selectedPrice = branch > 0 ? trade.high : trade.low
var mode = barIndex % 3
var switched = trade.close
switch (mode) {
case 0:
switched = trade.close
case 1:
switched = trade.high
default:
switched = trade.low
}
plotLine(value=selectedPrice + switched / 100 + loopTotal, colors=["#2563eb"], width=2, label=["Control flow"], desc=["if else for ternary and switch"])Loop iteration limit
Loops are capped at 1,000,000 iterations to keep a runaway loop from hanging the chart. Exceed it and the script stops with Loop iteration limit exceeded (max 1000000). In practice you'll never approach this with normal window sizes; it's a guardrail against an accidental infinite loop.
//@version=2
define(title="Loop Iteration Boundary", position="offchart", axis=true)
timeseries trade = ohlcv(symbol=currentSymbol, exchange=currentExchange)
if (isLastBar) {
var total = 0
for (var n = 0; n < 1000001; n += 1) {
total += n
}
}
plotLine(value=trade.close, colors=["#dc2626"], width=2, label=["Close"], desc=["anchor for loop iteration boundary"])What to keep in mind
- Per-bar execution. Everything here re-runs on every bar. To carry state across bars, declare it with
persist(see Execution Model); a plainvarresets each bar. whileexists but use it sparingly. A countedforis clearer and safer when you know the bound. Reach forwhileonly for genuine search-until-found logic, and make sure the condition can't get stuck.- No timeseries in local scope. Declare every
timeseriesat the top level, then read history with[n]indexing inside loops and branches.