Skip to main content

Built-in Namespaces

Every built-in capability lives under a namespace. This is the reference for all of them. For language syntax (variables, control flow) see the Language Reference.

Bar series​

Top-level price/volume series — a value per bar, indexable with [N].

IdentifierMeaning
closeClosing price
openOpening price
highHigh price
lowLow price
volumeVolume
gap_up = open > close[1]          # today's open above yesterday's close
range_today = high - low

ta — technical analysis​

FunctionReturnsNotes
ta.sma(series, length)numberSimple moving average
ta.ema(series, length)numberExponential moving average
ta.wma(series, length)numberWeighted moving average
ta.rsi(series, length)numberRelative strength index
ta.atr(length)numberAverage true range
ta.adr(length)numberAverage daily range %
ta.macd(series, fast, slow, signal)tuple (macd, signal, hist)MACD
ta.bbands(series, length, mult)tuple (upper, middle, lower)Bollinger Bands
ta.highest(series, length)numberHighest value over last N bars
ta.lowest(series, length)numberLowest value over last N bars
ta.crossover(a, b)booleantrue only on the bar a crosses above b
ta.crossunder(a, b)booleantrue only on the bar a crosses below b
ta.change(series, length=1)numberseries[0] - series[length]
fast = ta.sma(close, 50)
slow = ta.sma(close, 200)
golden_cross = ta.crossover(ta.sma(close, 50), ta.sma(close, 200))
Coming in v1.1

ta.stoch, ta.vwap, ta.twap, and ta.intra are not yet available. For a crossover on derived values (not bare series), use the manual previous-difference pattern shown in Examples.

strategy — trade actions​

FunctionEffect
strategy.enter_long(...)Open a long position
strategy.enter_short(...)Open a short position
strategy.close_long()Close any open long
strategy.close_short()Close any open short
strategy.close_all()Close any open position

A position is opened or closed only by these calls. v1 allows one position per ticker — a second enter_long while already long does nothing. Closes always close the entire position.

Position sizing​

Entries take exactly one of these sizing keywords:

KeywordMeaning
size_pct=NAllocate N% of current equity. This compounds — as profits grow, each position grows.
cash=NDeploy a fixed dollar amount (shares = floor(N / price)). Does not compound.
qty=NBuy a fixed number of shares. Does not compound.
strategy.enter_long(size_pct=100)   # all-in, compounding
strategy.enter_long(cash=10000) # always $10,000
strategy.enter_long(qty=100) # always 100 shares
Don't compound? Use cash= or qty=

size_pct is a percentage of your growing equity, so it snowballs. If you want every trade to be the same size, use cash= or qty= — never try to fake it with size_pct.

Fill timing​

By default an order fills at the next bar's open (look-ahead safe). Override with one keyword:

KeywordFills at
(none)Next bar's open (default)
at_open=trueThis bar's open
at_close=trueThis bar's close
at_next_close=trueNext bar's close
strategy.enter_long(cash=10000, at_close=true)   # fill at today's close

position — current position state​

FieldValue when flat
position.size0 (positive = long, negative = short)
position.entry_pricena
position.unrealized_pnlna
position.unrealized_pnl_pctna
position.bars_heldna
if position.size > 0 and position.unrealized_pnl_pct > 10:
strategy.close_all()

input — tunable parameters​

Inputs become editable fields in the UI, so users can tune your strategy without editing code.

FunctionReturns
input.int(default, title)integer
input.float(default, title)float
input.bool(default, title)boolean
input.string(default, title, options=[...])string
length = input.int(14, "RSI Length")
threshold = input.float(30.0, "Oversold Level")

time — date & time​

Calendar and clock fields for the current bar.

FieldRange
time.hour0–23 (Eastern Time)
time.minute0–59
time.dayofweek0=Sunday, 1=Monday, 2=Tuesday … 5=Friday, 6=Saturday
time.dayofmonth1–31
time.dayofyear1–366
time.month1–12
time.yearfull year
time.unixUnix timestamp (milliseconds)
# Trade only on Tuesdays
if position.size == 0 and time.dayofweek == 2:
strategy.enter_long(cash=10000, at_close=true)
Day-of-week numbering

time.dayofweek is 0=Sunday, 1=Monday, 2=Tuesday, 3=Wednesday, 4=Thursday, 5=Friday, 6=Saturday. A common mistake is to treat 2 as Monday — it's Tuesday. On a daily timeframe, the value reflects the bar's trading date.

Pre-market & after-hours

On intraday timeframes, bars outside the regular session — pre-market (≈4:00–9:30 ET, time.hour < 9) and after-hours (≈16:00–20:00 ET, time.hour >= 16) — appear only when extended hours is enabled. Enable it from the header so the strategy can't silently no-op:

strategy("After-hours strategy", timeframe="5Min", extended_hours=true)

(Or toggle Extended Hours in the backtest settings.) With it off, only the regular session exists, so a pre/post-market strategy won't fire. Gate on time.hour to target a session. Note: extended-hours data must also exist for the ticker.

math — math helpers​

FunctionReturns
math.max(a, b, ...)max
math.min(a, b, ...)min
math.abs(x)absolute value
math.sqrt(x)square root
math.log(x)natural log
math.exp(x)e^x
math.floor(x)floor
math.ceil(x)ceiling
math.round(x)nearest integer

Any argument that is na makes the result na.

external — other tickers​

Read another symbol's price series from inside your strategy (read-only). Useful for market/regime filters.

FunctionReturns
external.close("TICKER")that ticker's close series
external.open / high / low / volume("TICKER")other OHLCV series

The ticker must be a string literal.

# Only go long when SPY is above its 200-day average
spy_uptrend = external.close("SPY") > ta.sma(external.close("SPY"), 200)

if spy_uptrend and position.size == 0 and rsi < 30:
strategy.enter_long(size_pct=10)

htf — higher-timeframe data​

Read a higher timeframe for the current ticker or a named symbol — e.g. the weekly trend while trading daily bars.

FunctionReturns
htf.close("1W")this ticker's close on the given timeframe
htf.open / high / low / volume("1W")other OHLCV fields
htf.close("SPY", "1W")a named symbol's series at a given timeframe

The timeframe (and optional symbol) must be string literals, and the timeframe must be equal to or higher than the backtest's base timeframe.

# Daily strategy gated by the weekly trend
weekly_sma = ta.sma(htf.close("1W"), 30)

if close > weekly_sma and position.size == 0:
strategy.enter_long(size_pct=10)
No look-ahead

A higher-timeframe bar only becomes visible once it has fully closed. While the current week is still forming, htf.close("1W") returns the last completed week's close — never a value from the future.

syminfo — current symbol​

FieldValue
syminfo.tickerthe symbol the script is currently running on (a string)

A backtest runs the same script once per ticker. syminfo.ticker lets one strategy apply different rules to different symbols:

if syminfo.ticker == "QQQ":
if position.size == 0 and time.dayofweek == 2: # QQQ: buy Tuesday
strategy.enter_long(cash=10000, at_close=true)
if position.size > 0 and time.dayofweek == 3: # sell Wednesday
strategy.close_all(at_close=true)
elif syminfo.ticker == "AAPL":
if position.size == 0 and time.dayofweek == 3: # AAPL: buy Wednesday
strategy.enter_long(cash=10000, at_close=true)
if position.size > 0 and time.dayofweek == 5: # sell Friday
strategy.close_all(at_close=true)

Run this over a ["QQQ", "AAPL"] universe and each symbol follows its own branch.

Every branch must place real orders

syminfo.ticker only chooses which rules apply — it does not move money on its own. Each branch that should trade must call strategy.enter_long / close_all / etc. Tracking a "position" with your own true/false variable places no trades and produces no results.