CODESCRIPT

Price data and series

Every script runs over bars. Once you know which prices a bar carries, and why those prices are called a series, the rest falls into place.

Each candle on the chart is one bar. A bar sums up a slice of time with four prices. Your script walks those bars left to right, running your whole code once on every one.

The prices inside a bar

Five basic series are waiting on every bar. Writing the name is enough; there is nothing to declare.

  • open — the first price the bar traded at.
  • high — the highest price seen during the bar, the tip of the upper wick.
  • low — the lowest price, the bottom of the lower wick.
  • close — the last price of the bar. Most rules are built on it.
  • volume — how much changed hands during the bar.
CODESCRIPT
// bir barin govdesi: kapanis eksi acilis
govde = close - open
// barin tam araligi: en yuksek eksi en dusuk
aralik = high - low
plot(govde, "govde", "#2962ff", "olcu")
plot(aralik, "aralik", "#ff9800", "olcu")

Two lines in the lower olcu panel: the blue one is the signed body size (positive on up bars, negative on down bars), the orange one is the full bar range including wicks.

TIP

close is the source you will reach for most often. In most indicator calls it is the first argument you see: sma(close, 20).

A series, or a single number?

A series is a column holding a separate value for each bar. A plain number stays the same on all of them. Getting this straight early spares you a lot of confusion later.

Compare a series with a number and you get a series back: one true or false per bar. Conditions are series as well, which is why one can hold on one bar and fail on the next.

CODESCRIPT
// sma bir SERI dondurur: her barda ayri bir deger
ortalama = sma(close, 20)
// esik tek bir SAYIdir: butun barlarda ayni kalir
esik = 70
// seriyi sayiyla karsilastirinca sonuc yine seridir
asiri = rsi(close, 14) > esik
plot(ortalama, "SMA20", "#2962ff")
plotshape(asiri, style="circle", location="abovebar", color="#ef5350")

A blue SMA20 line on the price panel, plus a red circle above every bar where RSI runs past 70.

CAUTION

plot draws numeric series only. Hand it a single number and you get a flat line; for a fixed level, hline is the better tool.

Derived price sources

The close on its own can be noisy, and a single wick tip is enough to upset a rule. Three ready-made series average the bar's prices for you.

  • hl2 — (high + low) / 2, the mid price of the bar.
  • hlc3 — (high + low + close) / 3, the typical price, which also weighs the close.
  • ohlc4 — (open + high + low + close) / 4, the smoothest source, weighting all four prices alike.
CODESCRIPT
// ayni uzunluk, uc farkli kaynak
plot(sma(close, 20), "close", "#787b86")
plot(sma(hl2, 20), "hl2", "#2962ff")
plot(sma(ohlc4, 20), "ohlc4", "#26a69a")

Three averages running close together on the price panel. The grey one wobbles most, the green one is the calmest.

NOTE

Derived sources cut noise at the cost of a slower reaction. Breakout rules usually want close, while channels and envelopes sit better on hl2.

Reading an earlier bar

Most rules compare now against a moment ago. There are two ways to reach a series' past value, and both give the same answer.

  • close[1] — the number in brackets says how many bars to step back. close[0] is the bar you are on.
  • prev(close, 1) — the function form of the same thing. It reads better when the offset itself is computed.
CODESCRIPT
// koseli parantez: sayi kac bar geriye gidilecegini soyler
oncekiKapanis = close[1]
// ayni degeri prev de verir
ayniDeger = prev(close, 1)
// ikisi her barda esit oldugu icin fark hep sifirdir
plot(oncekiKapanis - ayniDeger, "fark", "#787b86", "fark")
// ust uste iki yukselen kapanis
plotshape(close > close[1] and close[1] > close[2], style="triangleup", location="belowbar", color="#26a69a")

A flat line pinned at zero in the lower fark panel, proof that the two forms agree. On the price panel, green triangles under each bar that closes higher twice in a row.

prev(source, n=1)

A series carrying the value from n bars back.

source
series
The series whose earlier value you want.
n
number
How many bars to step back. Defaults to 1.

change(source, n=1)

The difference between the series now and n bars back.

source
series
The series whose movement you measure.
n
number
How far back the comparison reaches. Defaults to 1.
CAUTION

The further back you look, the longer your script takes to warm up. With close[3] there is nothing to read on the first three bars, so the result stays na there.

Series carry gaps too

A series does not have to hold a number on every bar. Where a value is missing there is na instead, and na entering a calculation turns the result to na as well. Nothing errors out here, the rule simply falls silent, so you use nz to fill the gap.

CODESCRIPT
// ilk 19 barda pencere dolmaz: sma na tasir
ortalama = sma(close, 20)
// na yerine 0 koy; cizgi bosluk yerine sifirdan baslar
plot(nz(ortalama, 0), "ortalama", "#2962ff")
// hacim eksik gelirse 0 say, sonra ortalamasiyla kiyasla
yogun = nz(volume, 0) > sma(volume, 20) * 2
plotshape(yogun, style="circle", location="bottom", color="#ff9800")

Instead of leaving a gap, the average line starts at zero for the first nineteen bars and then settles onto price. Orange circles appear at the bottom of the panel on bars where volume runs past twice its average.

TIP

Writing nz(volume, 0) keeps the rule alive on bars where volume comes in empty. The full story of missing values has its own topic; what matters here is simply that a series can carry gaps.

Time-of-bar series

Every bar also has a time identity: the hour, the day, the month it formed in. You read these as separate series, all worked out in the exchange timezone the symbol belongs to.

  • hour — the bar's hour, from 0 to 23.
  • minute — the bar's minute, from 0 to 59.
  • dayofweek — the day of the week, Sunday 1 through Saturday 7. Named constants such as dayofweek.monday work too.
  • dayofmonth — the day within the month.
  • month and year — the month (1-12) and the year.
  • weekofyear — which week of the year it is.

You will mostly use them as filters: holding trades inside certain hours, skipping the first minutes of a session, resetting a counter when a new month starts.

CODESCRIPT
// barin saati 0 ile 23 arasinda bir sayidir
sabahSaati = hour >= 8 and hour < 12
// haftanin gunu: Pazar 1, Cumartesi 7
haftaSonu = dayofweek == 1 or dayofweek == 7
plotshape(sabahSaati, style="circle", location="bottom", color="#2962ff")
plotshape(haftaSonu, style="square", location="top", color="#787b86")

Blue circles under the bars between 08:00 and 12:00, grey squares above the bars that fall on a weekend.

CODESCRIPT
// bu barin ayi onceki bardan farkliysa ayin ilk bari demektir
yeniAy = month != month[1]
// gecenin ilk saatlerinde giris yapma
sakinSaat = hour < 6
enterLong(crossover(close, sma(close, 20)) and not sakinSaat)
exit(crossunder(close, sma(close, 20)))
plotshape(yeniAy, style="flag", location="top", color="#ff9800")

An orange flag at the top of the panel on the first bar of each month. A buy opens only when the close crosses above the 20-bar average after 06:00; the position closes on the downward cross.

NOTE

Crypto bars arrive on UTC, and since trading never pauses, weekends produce bars as well. Confirm which timezone you are in before writing an hour filter; one written for the wrong zone quietly screens out the wrong bars.

What else a bar knows about itself

Beyond the clock there are a few more facts to read. They come with dot notation and usually serve to narrow a rule.

  • barstate.islast — is this the last bar. barstate.isconfirmed — has the bar closed.
  • timeframe.isdaily, timeframe.isintraday — which timeframe the chart is on. timeframe.period gives its text form.
  • syminfo.ticker — the symbol's code. syminfo.mintick — the smallest price step.
  • session.ismarket — whether the bar falls inside the regular session.
CODESCRIPT
// grafik gunluk mu, gun-ici mi
gunluk = timeframe.isdaily
plot(iff(gunluk, 1, 0), "gunluk mu", "#787b86", "bilgi")
// son bara bir elmas birak
plotshape(barstate.islast, style="diamond", location="top", color="#2962ff")

A flat line in the lower bilgi panel: 1 on a daily chart, 0 on any other timeframe. A blue diamond above the last bar on the right edge.

iff(condition, ifTrue, ifFalse)

A series that picks one of the two values on each bar.

condition
boolean
The condition tested on each bar; it may itself be a series.
ifTrue
series
The value taken while the condition holds.
ifFalse
series
The value taken while it does not.

Where beginners trip

  • Mistaking a series for a number. ortalama = sma(close, 20) does not produce one value but one per bar, and reading it as a single number makes the rule behave oddly.
  • Forgetting the opening bars. Until its window fills an indicator carries na, so the rule quietly does nothing there. Guard it with nz where it matters.
  • Hunting for a difference between close[1] and prev(close, 1). They return the same value; choosing between them is a matter of readability.
  • Reaching for a derived source without need. A breakout rule built on ohlc4 reacts late, because the wick tip dissolves into the average.
  • Writing an hour filter against the wrong timezone. It raises no error, it just screens out the wrong bars, which quietly skews a backtest.
  • Treating a condition as final before the bar closes. Values can still move on a forming bar, so pair the rule with barstate.isconfirmed when certainty matters.