Skip to main content

Language Reference

The syntax of Vaanam Script: how to write the script itself. For the built-in functions (ta.*, strategy.*, …) see Built-in Namespaces.

Script header​

Every script starts with a version directive and a strategy(...) declaration:

//@version=1
strategy("My Strategy", timeframe="1D")
  • //@version=1 — the language version. Put it on line 1.
  • strategy("Name", ...) — required exactly once. Optional timeframe kwarg pins the strategy to a specific resolution regardless of the UI selection:
    • Canonical: "1M" (1-minute), "5Min", "10Min", "15Min", "30Min", "1H", "4H", "1D", "1W", "1Month".
    • Aliases like "daily", "weekly", "1h" are accepted.
    • Note: "1M" is 1-minute, "1Month" is monthly. Don't write bare "M".

Comments​

// single-line comment (Pine-style)
# single-line comment (Python-style — also accepted)
/* multi-line
comment */

x = ta.rsi(close, 14) # mid-line comments work too

Both // and # work. The //@version=N directive is only recognized with the // marker.

Variables​

length = 14
trend_up = close > ta.sma(close, 200)
  • Plain name = expr declares a variable. There is no let/const.
  • Ordinary variables are bar-local — they reset every bar.
  • var name = expr declares a persistent variable that survives across bars. The initializer runs once (on the first bar); after that the variable keeps whatever value it was last assigned.
var highest_since_entry = na

if position.size > 0:
highest_since_entry = math.max(highest_since_entry, high)
tip

Forgetting var is the most common mistake — without it, your "running" value resets to its initializer every bar. If something should remember a value between bars, declare it with var.

Operators​

CategoryOperators
Arithmetic+ - * / % **
Comparison< > <= >= == !=
Logicaland or not
Indexingseries[N]

Standard precedence: ** binds tightest among arithmetic; not tighter than and, and tighter than or. Use () to group.

in_range = x > 0 and x < 100    # ✓
in_range = 0 < x < 100 # ✗ chained comparisons are NOT supported

Booleans are lowercase true / false (not True/False).

Series and indexing​

close, open, high, low, volume are series — a value per bar. Index them to look back:

  • close or close[0] — the current bar's close.
  • close[1] — the previous bar's close.
  • close[N] — N bars ago. If N exceeds available history, you get na.

In a comparison or arithmetic context a series automatically resolves to its current value, so close > 100 means close[0] > 100.

Control flow​

Indentation (4 spaces) defines blocks. Don't mix tabs and spaces.

if / elif / else​

if rsi < 30:
strategy.enter_long(size_pct=100)
elif rsi > 70:
strategy.close_all()
else:
# do nothing
x = 0

Use elif, not else if.

Loops​

for i in range(0, 5):
# i goes 0,1,2,3,4
total = total + close[i]

while cond:
...

Loops are capped per bar to keep backtests fast and safe.

Conditional expression (ternary)​

Python-style <then> if <cond> else <else>, as an expression (it produces a value):

size = 100 if rsi < 30 else 10

Use it to pick a value — not as a substitute for control flow. For actions with side effects (strategy.enter_long, etc.), use a full if statement.

# ✗ wrong — don't drive actions with a ternary
strategy.enter_long(size_pct=100) if rsi < 30 else strategy.close_all()

# ✓ right
if rsi < 30:
strategy.enter_long(size_pct=100)
else:
strategy.close_all()

Vaanam Script uses the Python if/else ternary, not Pine's cond ? a : b.

Functions​

Define reusable helpers at the top level with def:

def momentum(price, fast_len, slow_len):
fast = ta.sma(price, fast_len)
slow = ta.sma(price, slow_len)
return (fast - slow) / slow * 100

score = momentum(close, 10, 50)

In v1, functions return scalars (a single number/boolean), not series.

Tuple destructuring​

Multi-output indicators return tuples; unpack them with a comma assignment:

upper, middle, lower = ta.bbands(close, 20, 2)
macd, signal, hist = ta.macd(close, 12, 26, 9)

The na value​

na means "no value" — an indicator that hasn't warmed up, or a field that doesn't exist (like position.entry_price when flat).

  • Arithmetic with na yields na; comparisons with na yield false.
  • na == na is true — use it to test for na:
if not (prev_value == na):
# prev_value has a real value
...

What's intentionally not in the language​

To keep v1 small and safe, these do not exist: import/modules, classes, list/dict literals ([...], {...}), list comprehensions, file or network access, and Pine-specific constructs like request.security (use htf instead) or ?: ternaries.