---
title: Execution Model
description: How kScript runs your code bar by bar, where barIndex and isLastBar come from, and how history indexing works.
---

<div class="flex gap-3 mb-6">
  <span class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-blue-50 text-blue-600 text-sm font-medium">
    Core Concept
  </span>
  <span class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-gray-100 text-gray-600 text-sm font-medium">
    4 min read
  </span>
</div>

## The bar loop

kScript runs your whole script **once per bar**, oldest bar first, walking forward to the newest. Think of the body of your script as a loop body that the engine calls for every candle on the chart. On bar 0 it computes your plots for bar 0, on bar 1 it recomputes them for bar 1, and so on. The line you write once produces a value at every point in time, which is what gives you a series to draw.

That has one consequence worth internalizing: a source feed is what drives the loop. The engine builds its bar sequence from the data you load, so **a script with no source never enters the loop** and produces nothing. More on that below.

## barIndex and isLastBar

Two values let you reason about where you are in the walk:

- **`barIndex`** is the position of the current bar, starting at `0` on the oldest bar and counting up.
- **`isLastBar`** is `true` only on the most recent bar, and `false` everywhere else. Use it to do something exactly once, at the live edge of the chart (draw a label, snapshot a final total).

Both are available anywhere in the per-bar body, including inside ternaries and conditions.

## History indexing

Because the script re-runs each bar, you often want a previous bar's value: the close from one bar ago, the high from ten bars ago. That is **history indexing**, written `series[n]`, where `[1]` is one bar back and `[0]` is the current bar.

History indexing only works on a `timeseries` (or an array). It is the engine remembering past values of a series for you. A plain `var` holds just the current bar's scalar, so indexing it is a compile error. If you need `closeSeries[1]`, store the value in a `timeseries` first:

```javascript
timeseries trade = ohlcv(symbol=currentSymbol, exchange=currentExchange)
timeseries closeSeries = trade.close   // now closeSeries[1] is valid
```

See [Variables](core-variables.md) for the full `var` vs `timeseries` distinction.
## A complete example

This script ties the three ideas together. It anchors on `ohlcv`, reports `barIndex` (nudged up by one on the final bar so you can spot the live edge), and computes the one-bar change in close by indexing back through a `timeseries`.

```javascript title="Bar loop anchor" lines wrap
//@version=2
define(title="Bar Loop Anchor", position="offchart", axis=true)

timeseries trade = ohlcv(symbol=currentSymbol, exchange=currentExchange)
timeseries closeSeries = trade.close

var previousClose = barIndex > 0 ? closeSeries[1] : closeSeries
var closeDelta = closeSeries - previousClose
var loopValue = barIndex + (isLastBar ? 1 : 0)

plotLine(value=loopValue, colors=["#2563eb"], label=["barIndex"], desc=["barIndex with isLastBar offset"])
plotLine(value=closeDelta, colors=["#dc2626"], label=["closeDelta"], desc=["history indexing through a timeseries"])
```

What you'll see: the `barIndex` line is a straight ramp from `0` up to the last bar's index, ticking one higher on the final bar thanks to the `isLastBar` offset. The `closeDelta` line is the bar-over-bar price change, hovering around zero and spiking when price moves. Note the guard `barIndex > 0 ? closeSeries[1] : closeSeries`: on bar 0 there is no prior bar to look back to, so the script falls back to the current value instead.

## You need a source to reach the loop

Since source data is what generates the bar sequence, an offchart script that never opens a source has nothing to iterate. It will compile and start, then stop with zero bars to compute and a message telling you no source data reached the bar loop. Nothing draws, because there were no bars and therefore no plot points.

The fix is to add an anchor before you plot anything derived:

```javascript
//@version=2
define(title="Anchored", position="offchart", axis=true)

timeseries trade = ohlcv(symbol=currentSymbol, exchange=currentExchange)
// now the loop runs, && you can plot anything off trade
plotLine(value=trade.close, colors=["#2563eb"], label=["Close"], desc=["chart close"])
```
This applies even when your indicator doesn't obviously use price. If you compute something from time or constants alone, you still need a source open so the engine knows which bars to walk.

## var versus timeseries, at a glance

The split that matters in the bar loop is history:

- Use **`timeseries`** when you want history indexing: `closeSeries[1]`, `high[10]`. Source data and anything you plan to look back through belongs here.
- Use **`var`** when you only need the current bar's value: a scalar, a one-bar calculation, an indicator reading you consume immediately.

Indexing a `var` (`closeNow[1]`) fails at compile time with a message that indexing is only allowed on timeseries and arrays. That error is the engine telling you to promote the value to a `timeseries`. The full treatment, including `persist` for carrying state across bars, is in [Variables](core-variables.md).
