> ## Documentation Index
> Fetch the complete documentation index at: https://gocharting.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# History and state across languages

> How Lipi maps to MQL4, MQL5, NinjaScript, Pine Script and thinkScript for history and state: value on an earlier bar, bar number, last bar, how far back a script can read, history functions on every bar, keep a value across bars and more, with the differences that matter when converting.

Reading earlier bars and keeping values from one bar to the next. Each row maps one concept from Lipi to the other languages; the word in a cell is the relation, explained in the [language map overview](https://gocharting.com/docs/scripting/reference/language-map.md#relations).

| Concept | Lipi | MQL4 | MQL5 | NinjaScript | Pine Script | thinkScript |
| - | - | - | - | - | - | - |
| [Value on an earlier bar](#history-previous-value) | Syntax | Caveat | Differs | Caveat | Caveat | Caveat |
| [Bar number](#history-bar-index) | `bar_index` | Differs | Caveat | Exact | Exact | Caveat |
| [Last bar](#history-last-bar) | `last_bar_index`, `barstate.islast` | Caveat | Caveat | Caveat | Exact | Composite |
| [How far back a script can read](#history-lookback-limit) | Syntax | Differs | Caveat | Caveat | Caveat | Unresolved |
| [History functions on every bar](#history-call-every-bar) | `ta.sma`, `ta.ema` | Differs | Absent | Differs | Exact | Unresolved |
| [Keep a value across bars](#state-persistent) | `static` | Caveat | Differs | Differs | Exact | Caveat |
| [Keep a value across live updates](#state-intrabar) | `intra` | Caveat | Composite | Composite | Differs | Absent |
| [Bar state flags](#state-bar-flags) | `barstate.isfirst`, `barstate.ishistory`, `barstate.isrealtime`, `barstate.isnew`, `barstate.isconfirmed`, `barstate.islastconfirmedhistory` | Absent | Absent | Composite | Caveat | Absent |

Each section below gives the Lipi construct first, then the construct in each language, with a note wherever behaviour differs. The language name links to the documentation the row was checked against.

## Value on an earlier bar

Reading what a series was a given number of bars ago.

In Lipi this is written with the language's own syntax. x\[n] needs 0 \<= n \<= 500. A negative literal is a compile error; a computed bad index stops the script. Arrays use array.get.

```lipi theme={"languages":{"custom":["/languages/lipi.json","/languages/lipi-invalid.json"]}}
//@lipi=2
indicator("Earlier bars")
change = close - close[1]
plot(change, title="Change on the bar")
plot(math.max(high, high[1], high[2]), title="3-bar high", color=color.orange)
```

| Language | Construct | Relation | Note |
| - | - | - | - |
| [MQL4](https://docs.mql4.com/predefined/close) | `Close[n], iClose(symbol, timeframe, shift)` | Equivalent, with a caveat | Price series use shift 0 as current bar like Lipi. Own computed values need a buffer array; buffers index oldest-first unless ArraySetAsSeries flips them. History indexing: Indicator buffer arrays count from the oldest bar by default, opposite to price series. |
| [MQL5](https://www.mql5.com/en/docs/series/bufferdirection) | array\[index]; ArraySetAsSeries | Looks alike, behaves differently | Default arrays and buffers count from the oldest bar, so n bars back is i-n; series-flagged arrays count from the newest, so it is i+n. History indexing: Index 0 is the oldest bar by default but the newest in timeseries functions such as iClose and CopyRates. |
| [NinjaScript](https://ninjatrader.com/support/helpGuides/nt8/make_sure_you_have_enough_bars.htm) | `Close[barsAgo], Series<T>[barsAgo]` | Equivalent, with a caveat | Same direction as Lipi x\[n]: 0 is the current bar, 1 the previous one. Reading before the first bar throws, so guard with CurrentBar. Warm-up and missing values: Close\[1] on the first bar raises an out-of-range error; Lipi gives na. History indexing: Custom Series keep 256 values by default; Lipi allows 500. |
| [Pine Script](https://www.tradingview.com/pine-script-docs/language/operators/) | `[] history-referencing operator` | Equivalent, with a caveat | Pine buffers can reach back up to 5000 bars; Lipi stops the script at run time when the offset exceeds 500. |
| [thinkScript](https://toslc.thinkorswim.com/center/reference/thinkScript/Operators/Indexing) | x\[n]; x from n bars ago | Equivalent, with a caveat | Positive index is the past as in Lipi, but a negative index reads future bars, which Lipi rejects, so look-ahead scripts cannot be converted. History indexing: Negative index means a later bar; Lipi accepts only 0 or more. Warm-up and missing values: All expressions in a study share the highest past offset, so early bars start later than a single formula implies. |

## Bar number

The position of the current bar, counted from the first bar the script sees.

In Lipi: `bar_index`. An int series, 0 on the first bar the script receives.

| Language | Construct | Relation | Note |
| - | - | - | - |
| [MQL4](https://docs.mql4.com/predefined/close) | `Bars, shift index` | Looks alike, behaves differently | Index counts from the newest bar (0) to the oldest (Bars-1); there is no counter from the first bar, so bar number is Bars-1-shift. History indexing: Direction reversed: the current bar is 0 and bars grow older with the index. |
| [MQL5](https://www.mql5.com/en/docs/series/bufferdirection) | loop index i over rates\_total in OnCalculate | Equivalent, with a caveat | No bar\_index variable: you loop yourself, and index 0 is the first bar only while arrays keep the default direction. History indexing: After ArraySetAsSeries(true) the same index counts from the newest bar instead. |
| [NinjaScript](https://ninjatrader.com/support/helpGuides/nt8/currentbar.htm) | `CurrentBar` | Exact | |
| [Pine Script](https://www.tradingview.com/pine-script-reference/v6/#var_bar_index) | `bar_index` | Exact | |
| [thinkScript](https://toslc.thinkorswim.com/center/reference/thinkScript/Functions/Others/BarNumber) | `BarNumber()` | Equivalent, with a caveat | A function call, not a variable. The reference states no first-bar value; Lipi's bar\_index is 0 on the first bar. |

## Last bar

Identifying the most recent bar of the chart.

In Lipi: `last_bar_index`, `barstate.islast`. `barstate.islast` is true on the last loaded bar and on every realtime run; `last_bar_index` is that bar's index.

| Language | Construct | Relation | Note |
| - | - | - | - |
| [MQL4](https://docs.mql4.com/predefined/close) | `shift 0 (Close[0], Time[0])` | Equivalent, with a caveat | The last bar is shift 0; indicator loops test i==0 or start from a limit derived from prev\_calculated. There is no islast flag. |
| [MQL5](https://www.mql5.com/en/docs/series/bufferdirection) | rates\_total-1 (default arrays); index 0 (series arrays) | Equivalent, with a caveat | No flag exists: the last bar is the final element in default arrays and index 0 in series-flagged arrays or timeseries functions. |
| [NinjaScript](https://ninjatrader.com/support/helpGuides/nt8/count.htm) | `CurrentBar == Count - 1` | Equivalent, with a caveat | No islast flag; compare CurrentBar with Count - 1. Count can run ahead of CurrentBar because ticks arrive on other threads. Bar timing: CurrentBar is only guaranteed to be at most Count - 1, so equality is not guaranteed in real time. |
| [Pine Script](https://www.tradingview.com/pine-script-reference/v6/) | `last_bar_index, barstate.islast` | Exact | |
| [thinkScript](https://toslc.thinkorswim.com/center/reference/thinkScript/tutorials/Advanced/Chapter-12---Past-Offset-and-Prefetch) | `!IsNaN(close) and IsNaN(close[-1])` | Built from several Lipi constructs | No built-in last-bar flag: the documented idiom tests that the next bar's close is NaN, which uses a future index; map to barstate.islast. Bar timing: The idiom relies on a look-ahead index; the HighestAll alternative forces once-per-bar recalculation. |

## How far back a script can read

The largest number of bars a script may reference in the past.

In Lipi this is written with the language's own syntax. Reach is 0 to 500 bars. A longer offset or length compiles, then stops the script; ema-family lengths above 500 fail on Add to chart.

```lipi theme={"languages":{"custom":["/languages/lipi.json","/languages/lipi-invalid.json"]}}
//@lipi=2
indicator("Furthest look back", overlay=true)
// 500 is the largest offset a script may read
plot(bar_index >= 500 ? close[500] : na, title="Close 500 bars ago")
```

| Language | Construct | Relation | Note |
| - | - | - | - |
| [MQL4](https://docs.mql4.com/constants/environment_state/terminalstatus#TERMINAL_MAXBARS) | `TERMINAL_MAXBARS` | Looks alike, behaves differently | No fixed 500-bar cap; reach is bounded by the number of bars the chart holds, a terminal setting read with TerminalInfoInteger. History indexing: Lookback depth is limited by loaded history size, not by a small constant. |
| [MQL5](https://www.mql5.com/en/docs/series/timeseries_access) | `TERMINAL_MAXBARS (Max bars in chart)` | Equivalent, with a caveat | Reach is bounded by the bars loaded for the chart (terminal setting), not a fixed 500; OnCalculate arrays hold all loaded bars. |
| [NinjaScript](https://ninjatrader.com/support/helpGuides/nt8/maximumbarslookback.htm) | `MaximumBarsLookBack` | Equivalent, with a caveat | Custom Series default to the last 256 values (ring buffer); Infinite lifts it. Price and plot series are always unlimited, unlike Lipi's flat 500. History indexing: Beyond 256 on a custom series, the oldest values are overwritten rather than raising an error. |
| [Pine Script](https://www.tradingview.com/pine-script-docs/writing/limitations/#maximum-bars-back) | historical buffer (auto-sized); max\_bars\_back() and the max\_bars\_back argument | Equivalent, with a caveat | Pine sizes buffers automatically, up to 5000 bars (10000 for open, high, low, close, time), and max\_bars\_back() raises them. Lipi has a fixed 500-bar limit. History indexing: Pine reaches 5000 bars back for most series and 10000 for price series and time; Lipi stops at 500. |
| [thinkScript](https://toslc.thinkorswim.com/center/reference/thinkScript/tutorials/Advanced/Chapter-12---Past-Offset-and-Prefetch) | | Unresolved | The reference states no maximum look-back distance; it only describes past-offset initialisation, so Lipi's 500-bar cap has no documented counterpart. |

## History functions on every bar

Functions that keep their own history give correct results only when called on every bar, not inside a branch that skips bars.

In Lipi: `ta.sma`, `ta.ema`. A talib call in a skipped if branch, an unchosen ?: side or a sometimes-called function is computed over its own calls, not bars.

```lipi theme={"languages":{"custom":["/languages/lipi.json","/languages/lipi-invalid.json"]}}
//@lipi=2
indicator("Call on every bar", overlay=true)
fast = talib.sma(close, 10)
slow = talib.ema(close, 20)
useFast = bar_index % 2 == 0
plot(useFast ? fast : slow, title="Alternating average")
```

| Language | Construct | Relation | Note |
| - | - | - | - |
| [MQL4](https://docs.mql4.com/indicators/ima) | iMA(symbol, timeframe, ma\_period, ma\_shift, ma\_method, applied\_price, shift) | Looks alike, behaves differently | Indicator functions read history by shift and keep no per-call-site state, so skipping bars changes nothing. ma\_shift offsets the line; shift picks the bar. State and side effects: Stateless recomputation from history; conditional calls are safe. |
| [MQL5](https://www.mql5.com/en/docs/indicators/ima) | | Not in MQL5 | No per-call-site history exists: indicator values come from a handle (iMA) read with CopyBuffer, so skipping bars in your own loop breaks nothing. |
| [NinjaScript](https://ninjatrader.com/support/helpGuides/nt8/update.htm) | `Update()` | Looks alike, behaves differently | Hosted indicators update when the host accesses them; Update() catches one up to the current bar. Lipi history functions need a call every bar. State and side effects: Hoist indicator calls made only inside a branch to top level when converting to Lipi. |
| [Pine Script](https://www.tradingview.com/pine-script-docs/language/execution-model/#time-series-in-scopes) | ta.sma(), ta.ema() called in the global scope | Exact | |
| [thinkScript](https://toslc.thinkorswim.com/center/reference/thinkScript/Reserved-Words/if) | | Unresolved | An if-expression always computes both branches, an if-statement only the taken one; docs do not say how history functions behave in a skipped branch. |

## Keep a value across bars

A variable that is set once and keeps its value from bar to bar until the script changes it.

In Lipi: `static`. Committed when the bar closes. Each tick of a forming bar restarts from the saved value, so a push per tick adds once per bar.

```lipi theme={"languages":{"custom":["/languages/lipi.json","/languages/lipi-invalid.json"]}}
//@lipi=2
indicator("Highest high so far", overlay=true)
static float top = na
top := na(top) ? high : math.max(top, high)
plot(top, title="Top")
```

| Language | Construct | Relation | Note |
| - | - | - | - |
| [MQL4](https://docs.mql4.com/basis/variables/static) | `static and global variables` | Equivalent, with a caveat | static and global variables keep values between handler calls and are initialised once at load; every tick updates them, with no rollback to bar close. State and side effects: No commit-on-close: tick writes stay, unlike Lipi static on a forming bar. |
| [MQL5](https://www.mql5.com/en/docs/basis/variables/static) | static (local variable); global variables | Looks alike, behaves differently | static and globals keep every change from every call, including repeated calls on the forming bar; nothing rolls back at bar close. State and side effects: Initialised once; the value persists across all later calls, not only confirmed bars. |
| [NinjaScript](https://ninjatrader.com/support/helpGuides/nt8/developing_indicators.htm) | `class-level field` | Looks alike, behaves differently | Fields keep their value across every OnBarUpdate call, including intrabar ticks. Lipi static rolls back to the last closed-bar value on each live tick. State and side effects: A field updated on every tick accumulates; the same logic in a Lipi static restarts from the committed value each tick. |
| [Pine Script](https://www.tradingview.com/pine-script-docs/language/variable-declarations/) | `var` | Exact | |
| [thinkScript](https://toslc.thinkorswim.com/center/reference/thinkScript/Reserved-Words/def) | def x = x\[1] + ...; rec | Equivalent, with a caveat | Persistence is self-reference with \[1] on a def (rec is obsolete). It starts at 0 unless CompoundValue seeds it; there is no static keyword. Warm-up and missing values: An unseeded self-reference starts from 0, not na. |

## Keep a value across live updates

A variable that also keeps its value between updates of the same live bar.

In Lipi: `intra`. `intra` is cleared when the bar closes. On history, with one run per bar, it behaves like an ordinary variable.

```lipi theme={"languages":{"custom":["/languages/lipi.json","/languages/lipi-invalid.json"]}}
//@lipi=2
indicator("Updates of the live bar")
intra int updates = 0
updates += 1
plot(updates, title="Updates")
```

| Language | Construct | Relation | Note |
| - | - | - | - |
| [MQL4](https://docs.mql4.com/basis/variables/static) | `static or global variable` | Equivalent, with a caveat | All persistent variables survive ticks and are never cleared at bar close; reset by comparing a saved bar time to Time\[0]. State and side effects: No automatic clearing when a new bar starts. |
| [MQL5](https://www.mql5.com/en/docs/basis/variables/global) | global variable; static local | Built from several Lipi constructs | No separate keyword: values persist through all calls, so clearing state when a new bar starts needs your own new-bar test. |
| [NinjaScript](https://ninjatrader.com/support/helpGuides/nt8/isfirsttickofbar.htm) | class-level field reset when IsFirstTickOfBar is true | Built from several Lipi constructs | Fields already persist across ticks of one bar but are never cleared at bar close; reset them on IsFirstTickOfBar to mimic Lipi intra. |
| [Pine Script](https://www.tradingview.com/pine-script-docs/language/variable-declarations/) | `varip` | Looks alike, behaves differently | varip keeps its value across ticks without rollback and also across bars; Lipi's intra is cleared when a new bar starts. State and side effects: Pine varip never resets between bars; Lipi intra restarts from its initializer on each new bar. |
| [thinkScript](https://toslc.thinkorswim.com/center/reference/thinkScript/Declarations/once-per-bar) | | Not in thinkScript | No intrabar state construct is documented; the only control is recalculation on every tick or, with once\_per\_bar, once per bar. |

## Bar state flags

True/false values that tell the script whether the bar is the first, historical, live, new or closed.

In Lipi: `barstate.isfirst`, `barstate.ishistory`, `barstate.isrealtime`, `barstate.isnew`, `barstate.isconfirmed`, `barstate.islastconfirmedhistory`. `barstate.isnew` is true on every history bar. `isconfirmed` is false on the last history bar while the market is open.

| Language | Construct | Relation | Note |
| - | - | - | - |
| [MQL4](https://docs.mql4.com/predefined) | | Not in MQL4 | No first, history, new-bar or confirmed flags among predefined variables; programs compare Time\[0] with a saved time, and indicators read IndicatorCounted. |
| [MQL5](https://www.mql5.com/en/docs/event_handlers/oncalculate) | | Not in MQL5 | No bar-state flags: use prev\_calculated (0 means full recalculation) and rates\_total to find new or changed bars. |
| [NinjaScript](https://ninjatrader.com/support/helpGuides/nt8/state.htm) | `State, IsFirstTickOfBar, Bars.IsFirstBarOfSession, CurrentBar` | Built from several Lipi constructs | ishistory/isrealtime = State.Historical/Realtime; isnew = IsFirstTickOfBar; isfirst = CurrentBar == 0, not IsFirstBarOfSession; no isconfirmed flag. Bar timing: A bar is known closed only when the next bar's first tick arrives; read Close\[1] on that tick. |
| [Pine Script](https://www.tradingview.com/pine-script-reference/v6/) | `barstate.isfirst, ishistory, isrealtime, isnew, isconfirmed, islastconfirmedhistory` | Equivalent, with a caveat | While the market is open, Lipi's isconfirmed is false on the last history bar and islastconfirmedhistory is false on every history bar. |
| [thinkScript](https://toslc.thinkorswim.com/center/reference/thinkScript/tutorials/Advanced/Chapter-12---Past-Offset-and-Prefetch) | | Not in thinkScript | No bar-state flags: the first bar comes from BarNumber() (base unstated), the last from the IsNaN next-bar idiom; no live or confirmed-bar test. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.