---
title: Control Flow
description: if/else, for loops, ternary expressions, and switch, the building blocks of per-bar logic in kScript.
---

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.

```javascript
//@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:

```javascript
// 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 : na
```
## for 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`.

```javascript
// Sum the last 5 closes by hand
var total = 0
for (var n = 0; n < 5; n += 1) {
  total += trade.close[n]
}
```



{% hint style="info" %}
You cannot declare a `timeseries` inside a loop (or any local scope); timeseries must live at global scope. Loop bodies work with scalar `var`s and historical access like `trade.close[n]`.

{% endhint %}
## switch

`switch` matches a value against `case` labels with a `default` fallback. Use it to map a discrete code to a value or branch.

```javascript
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.

```javascript title="scripts/probes/lang-controlflow/controlflow_happy.ks"
//@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.

```javascript title="scripts/probes/lang-controlflow/loop_iteration_limit_boundary.ks"
//@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](../core-concepts/execution-model.md)); a plain `var` resets each bar.
- **`while` exists but use it sparingly.** A counted `for` is clearer and safer when you know the bound. Reach for [`while`](./loops.md#while-loop) only for genuine search-until-found logic, and make sure the condition can't get stuck.
- **No timeseries in local scope.** Declare every `timeseries` at the top level, then read history with `[n]` indexing inside loops and branches.
