# WickScript language guide

> The complete WickScript syntax – head line, names, numbers and units, operators, conditions, if and when blocks, functions, loops, inputs and output statements.

Updated: 2026-10-25T00:01:51.000Z · https://chartrider.com/docs/wickscript/language/

This page describes everything the WickScript language accepts. Each example is a complete script you can paste into **Strategy Lab → Scripts**. For strategy statements (`enter`, `stop at`, `exit` …) see [Strategies](https://chartrider.com/docs/wickscript/strategies/); for other timeframes, sessions and orderflow see [Data](https://chartrider.com/docs/wickscript/data/).

## The head of a script

Every script starts with two lines:

1. the **version line** `wickscript 1`, and
2. the **head line** that says what the script is.

```text
wickscript 1
indicator "Name" on chart      # draws in the price chart (the default)
indicator "Name" in pane       # draws in its own panel below the chart
strategy "Name" risk 1%        # a strategy, with options
```

The version line is required and must come first; only comments and blank lines may stand before it. Version 1 is the only version; a script always runs with the rules of its version, so later language versions never silently change its results.

The name in quotes becomes the script's name in the chart legend and in the indicator settings. Strategy options (`risk`, `leverage`, `cooldown`, `minR`) are described on the [Strategies](https://chartrider.com/docs/wickscript/strategies/) page.

```wickscript
# comments may stand before the version line
wickscript 1
indicator "Candle range" in pane
show high - low as histogram "Range"
```

## Lines, blocks and comments

- **One statement per line.** A `;` separates several statements on one line.
- **A line continues** while a bracket `(` or `[` is open, or when it ends with an operator such as `+`, `*`, `and`, `or` or `>`.
- **Blocks end with `end`.** Indentation has no meaning; Format indents blocks by four spaces for readability.
- **Comments** start with `#` or `//` and run to the end of the line. `/* … */` comments can span several lines. Comments may stand behind a statement.

```wickscript
wickscript 1
indicator "Layout" on chart
/* a comment over
   several lines */
mid = (high + low) / 2   # a comment behind a statement
upper = mid +
    atr(14)              // the line continues after the +
a = 1; b = 2
show mid as line "Mid"
show upper as line "Upper"
```

A `#` directly followed by exactly six hex digits is a color, not a comment: `#deface` is the color `#deface`. Put a space after `#` when you write comments (`# deface`).

## Names

- Names consist of the letters A–Z and a–z, the digits 0–9 and `_`, and start with a letter or `_`. Other letters (ä, ß, é …) are not allowed.
- **Names ignore upper and lower case**, for your own names, built-in names and keywords: `myEma`, `MYEMA` and `myema` are the same variable, `EMA(Close, 9)` is `ema(close, 9)`. `_` counts: `my_ema` and `myema` are different names. If you spell a name differently from its first definition, the editor shows a quiet hint and Format unifies the spelling.
- You can't use a built-in name or a keyword for your own variable: `close = 5` is an error ("close is built in"), and so is `when = 5`.
- Words that only have a meaning at one place – `from`, `to`, `step`, `line`, `color`, `above`, `below`, `stop`, `target`, `long`, `level`, `hud` and similar – stay free as names. `stop = lowest(low, 5)` is an ordinary variable.

The reserved keywords are: `wickscript indicator strategy library keep if then else end when repeat define return show alert mark use input and or not true false na mod crosses is rising falling becomes on of as`.

Unknown names are errors with a suggestion: `emma(close, 9)` gives "There is no function emma – did you mean ema?".

## Values and literals

| Literal | Meaning | Unit |
|---|---|---|
| `14`, `0.25` | a plain number (decimal point, no thousands separator) | number |
| `0.3%` | a percentage | percent |
| `2R`, `1.5R` | a multiple of the trade's risk | R |
| `5 bars`, `1 bar` | a number of candles | bars |
| `1 tick`, `3 ticks` | that many times the market's price step | price |
| `30s`, `15m`, `4h`, `1d`, `1w`, `1M` | a time span: seconds, minutes, hours, days, weeks, months (30 days); `1mo` = `1M` | duration |
| `"London sweep"` | text in double quotes, on one line; `\"`, `\\`, `\n`, `\t` inside | text |
| `amber`, `#ff8800` | a color name or `#rrggbb` | color |
| `true`, `false` | yes / no | bool |
| `na` | no value | – |

Lower-case `m` is minutes, upper-case `M` is months. A letter directly behind a number must be a known suffix: `14abc` is an error; write a space before a name.

Color names: `white`, `black`, `gray`, `red`, `green`, `blue`, `amber`, `orange`, `yellow`, `lime`, `teal`, `cyan`, `purple`, `violet`, `pink`.

## Units

Every number carries a unit, and the checker rejects calculations that mix units – adding a volume to a price, comparing an RSI with a price, or using a plain number where a price distance is meant. A unit problem is always an **error**, and the message says how to write it correctly.

| Unit | Examples |
|---|---|
| price | `close`, `high`, `ema(close, 20)`, `vwap`, `atr(14)` (a price distance), `2 * tick`, `price(2000)` |
| percent | `0.3%`, `pct(close, close[1])`, `position.size` |
| amount (volume) | `volume`, `buyVolume`, `delta`, `obv`, `book.bidVolume(10)`, `amount(50)` |
| bars (candles) | `5 bars`, `bars since (close > open)`, `position.bars` |
| R | `2R`, `position.r` |
| time | `time`, `trade.time` |
| duration | `15m`, `4h`, `time - time[1]` |
| number (no unit) | `14`, `rsi(close, 14)`, `mfi(14)`, `hour`, `close / open`, `book.imbalance(10)` |
| bool, text, color | `close > open`, `session`, `amber` |

How units combine:

| Calculation | Result |
|---|---|
| price ± price, price × number, price / number | price |
| price ± percent | price, relative to the price: `close + 0.3%` is `close × 1.003` |
| price × percent | price: `atr(14) * 50%` is half the ATR |
| price / price, amount / amount (same units) | number (a ratio) |
| number × anything, anything / number | the unit of the other side |
| time − time | duration |
| time ± duration | time |
| bars `mod` number, number `mod` number | bars, number |
| price + number, price + amount, price × price, number / price | **error** |

Comparisons (`>`, `==` …) and `crosses` only work between values of the same unit. Text, colors and yes/no values can only be compared with `==` and `!=`.

```wickscript
wickscript 1
indicator "Units" on chart
buffer = close + 0.3%                    # price + percent = price
band = ema(close, 20) + 2 * atr(14)      # price + number × price = price
ratio = close / open                     # price / price = number
bigBody = abs(close - open) > atr(14)    # price > price
loud = volume > 2 * sma(volume, 20)      # amount > amount
level2000 = close > price(2000)          # a fixed price level
show buffer as line "Buffer"
show band as line "Band"
```

Typical errors and their fixes:

```text
close + 5              → Can't add a price and a plain number – write 5 * tick or 5%
close > 2000           → Can't compare a price with a plain number – write price(2000) for a fixed price level
close > volume         → Can't compare a price with an amount (volume)
bars since x >= 10     → write "10 bars"
target at 2            → Expected a target price or an R multiple like 2R, not a plain number
trade.size > 50        → write amount(50) for a fixed amount (volume)
close * close          → Can't multiply a price by a price – use number(…) if you really mean plain numbers
```

Conversion functions make your intent visible:

- `price(2000)` – a fixed price level, `amount(50)` – a fixed volume.
- `number(x)` – drops the unit of `x` on purpose, e.g. `number(close) * number(volume)`.
- `pct(now, before)` – the change from `before` to `now` in percent: `pct(close, close[1])`.

A variable keeps the unit of its first assignment: `y = close` followed by `y = volume` is an error. A sum of prices starts with `price(0)`, a sum of volumes with `amount(0)`:

```wickscript
wickscript 1
indicator "Sum of closes" in pane
total = price(0)
repeat i from 0 to 4
    total = total + close[i]
end
show total / 5 - sma(close, 5) as line "Difference"
```

## Variables

`name = expression` creates or changes a variable. A normal variable starts **every candle** with `na` and gets its value when the assignment runs on that candle. A variable assigned only inside an `if` therefore has no value (`na`) on candles where the `if` didn't run – which is how you show a line only on some candles.

`keep name = value` creates a variable that **keeps its value from candle to candle** (like `var` in Pine Script). The value after `keep` is used once, on the first candle; later candles start with the value of the previous candle. Use `keep` only on the first assignment; change the variable afterwards with a normal `=`.

```wickscript
wickscript 1
indicator "Green candle streak" in pane
keep streak = 0
if close > open then
    streak = streak + 1
else
    streak = 0
end
show streak as histogram "Streak"
```

### History: `[n]`

`x[n]` is the value of `x` `n` candles ago: `close[1]` is the previous close, `ema(close, 20)[3]` the EMA three candles ago, and `myVar[1]` the value your variable had at the end of the previous candle. `[0]` is the current value.

- `n` must be 0 or more – a negative offset would look into the future and is an error.
- `n` must be fixed: a number, an input, a variable assigned once to a constant, or the counter of a `repeat` loop that starts at 0 or higher.
- Before the first candle there is no history: `close[1]` on the first candle is `na`.
- History works for every built-in value, including the time values: `hour[1]` is the hour of the previous candle, `session[1] is London` asks whether the previous candle was in London.

```wickscript
wickscript 1
indicator "Momentum" in pane
len = input 10 "Length"
show close - close[len] as histogram "Momentum"
show (close - close[len])[1] as line "Previous"
```

## Operators

From the weakest to the strongest binding:

| Level | Operators |
|---|---|
| 1 | `or` |
| 2 | `and` |
| 3 | `not` |
| 4 | comparisons `== != < <= > >=` and phrases: `crosses above`, `crosses below`, `is`, `is not`, `is between … and …`, `rising`, `falling`, `for … bars`, `becomes true` / `becomes false` |
| 5 | `+ -` |
| 6 | `* / mod` |
| 7 | minus sign `-x` |
| 8 | function call `f(…)`, history `[n]`, field `.name` or `["name"]`, output `["Name"]`, `on 4h`, `of BTCUSDT` |

- `mod` is the remainder: `hour mod 4 == 0`. There is no `%` operator; `%` only follows a number (`0.5%`).
- Division by zero gives `na`.
- Compare two values at a time: `30 < r < 70` is an error; write `r > 30 and r < 70` or `r is between 30 and 70`.
- `not` binds weaker than comparisons: `not close > open` means `not (close > open)`.
- `on` and `of` bind to the value directly in front of them: `close - open on 4h` is `close - (open on 4h)`. Use brackets for a whole calculation: `(close - open) on 4h`.
- Text comparisons ignore upper and lower case: `session == "london"` is true in the London session.

## Conditions and phrases

Conditions are yes/no values. Store them in variables, combine them with `and`, `or`, `not`, and use them in `if`, `when`, `show … as event` and strategy statements.

| Phrase | True when |
|---|---|
| `a crosses above b` | `a` is above `b` now and was at or below `b` on the previous candle |
| `a crosses below b` | `a` is below `b` now and was at or above `b` on the previous candle |
| `x is y`, `x is not y` | `x == y`, `x != y` |
| `x is A or B` | `x == A or x == B` (alternatives are plain names or texts) |
| `x is between lo and hi` | `lo <= x <= hi` (both ends included) |
| `x rising 3 bars` | `x` is higher than each of its last 3 values |
| `x falling 3 bars` | `x` is lower than each of its last 3 values |
| `cond for 3 bars` | `cond` is true on this candle and the 2 candles before |
| `cond becomes true` | `cond` is true now and was not on the previous candle |
| `cond becomes false` | `cond` is false now and was true on the previous candle |
| `bars since (cond)` | the number of candles since `cond` was last true (0 on that candle, `na` before the first time) |

`crossover(a, b)` and `crossunder(a, b)` are the same as `a crosses above b` and `a crosses below b`. The number of candles after `rising`, `falling` and `for` is written with `bars` and must be fixed (a number or an input).

`bars since` takes the value right after it, so put a condition in brackets: `bars since (close > open)`. In strategies, `bars since entry` counts the candles since the entry of the open position.

```wickscript
wickscript 1
indicator "Phrases" in pane
r = rsi(close, 14)
show r crosses above 50 as event "RSI up"
show r is between 40 and 60 as event "RSI neutral"
show ema(close, 20) rising 5 bars as event "EMA rising"
show close > open for 3 bars as event "Three green"
show (volume > 2 * sma(volume, 20)) becomes true as event "Volume spike"
show bars since (r > 70) as line "Since overbought"
show weekday is Saturday or Sunday as event "Weekend"
```

`x for 3 bars` applies to the comparison directly in front of it: `close > open for 3 bars` means `(close > open) for 3 bars`. With `and`/`or`, use brackets: `(close > open and volume > volume[1]) for 3 bars`.

## Missing values: `na`

`na` means "no value": the first candles of a moving average, `close[1]` on the first candle, a variable that wasn't assigned on this candle, or orderflow data where none was recorded.

- Calculating with `na` gives `na`: `na + 1` is `na`.
- **Every comparison with `na` is false**, including `!=`. `x == na` is never true – use `isna(x)`.
- In `and`, `or`, `if` and `when`, a missing condition counts as false.
- `nz(x)` replaces `na` with 0, `nz(x, y)` with `y` (which must have the same unit as `x`).
- A line shows nothing on candles where its value is `na`.
- `sma`, `wma`, `highest`, `lowest`, `sum` and `stdev` are `na` until their window is full, and while the window contains a missing value. `ema` and `rma` start with the average of the first `length` values and keep their last value over missing values.

```wickscript
wickscript 1
indicator "Warm-up" in pane
slow = sma(close, 50)
ready = not isna(slow)
show ready as event "Ready"
show nz(delta) as histogram "Delta or 0"
```

## `if … then … else … end`

`if` is a **state**: its block runs on every candle on which the condition is true. `then` is optional. `else if` continues the chain, and the whole chain is closed by one `end`. A short `if` fits on one line.

```wickscript
wickscript 1
indicator "Candle type" in pane
kind = 0
if close > open then
    kind = 1
else if close < open then
    kind = -1
else
    kind = 0
end
big = 0
if high - low > 2 * atr(14) then big = 1 end
show kind as histogram "Type"
show big as line "Big"
```

## `when … end`

`when` is an **event**: its block runs only on the candle on which the condition **becomes** true – it was false (or `na`) on the previous candle. While the condition stays true, the block doesn't run again; it runs again after the condition has been false for at least one candle.

```wickscript
wickscript 1
indicator "Above the EMA" on chart
e = ema(close, 50)
show e as line "EMA 50" color amber
when close > e
    mark "up" below color green      # once, when the close gets above the EMA
end
when close < e
    mark "down" above color red
end
```

With `if close > e` instead, the mark would be written on every candle above the EMA. For conditions that are already events, like `crosses above`, `if` and `when` behave the same. Alerts, marks, levels and strategy entries normally belong in a `when` block; the checker warns when they would run on every candle.

**Functions keep counting inside blocks.** Built-in functions with a history (`ema`, `rsi`, `highest`, `crosses` …) update on every candle, even inside an `if` or `when` block that doesn't run on that candle, and even on the side of an `and`/`or` that isn't evaluated. `lowest(low, 10)` inside a `when` block is always the lowest low of the last 10 candles.

## Loops: `repeat`

`repeat i from a to b … end` runs its block with `i` = `a`, `a + 1`, … `b`. The bounds are plain numbers; the counter can't be changed in the loop. A loop runs at most 500 times, and one candle can run at most 10,000 loop steps over all loops together – beyond that, the loops stop for that candle and the Scripts tab shows a message with the line of the loop.

```wickscript
wickscript 1
indicator "Green candles of the last 10" in pane
count = 0
repeat i from 0 to 9
    if close[i] > open[i] then count = count + 1 end
end
show count as histogram "Green"
```

The counter can be used as a history offset (`close[i]`) when the loop starts at 0 or higher.

## Functions: `define`

Define your own functions at the top level of the script. A one-line function returns its expression; a multi-line function ends with `end` and returns its result with `return`.

```wickscript
wickscript 1
indicator "Own functions" on chart
length = input 20 "Length"
define mid(a, b) = (a + b) / 2
define band(src, mult)
    basis = sma(src, length)
    return basis + mult * stdev(src, length)
end
show mid(high, low) as line "Mid"
show band(close, 2) as line "Upper band"
show band(hl2, 3) as line "Wide band"
```

- Functions may be called before their definition. Arguments are passed in order, without names.
- **Units are worked out per call**: `mid(high, low)` returns a price, `mid(volume, volume[1])` an amount. A unit error inside the function is reported at the call.
- Every call has its own state: `band(close, 2)` and `band(hl2, 3)` keep separate moving averages.
- **Lengths and history offsets inside a function must be fixed numbers or the script's inputs**, not parameters: `define f(src, len) = sma(src, len)` is an error, `define f(src) = sma(src, length)` with `length = input 20 "Length"` works.
- Variables inside a function are local; the function can also read the script's own variables.
- All `return` values must have the same unit. A function can't call itself.
- Inside a function you can calculate (`=`, `keep`, `if`, `when`, `repeat`), but not use `input`, `show`, `level`, `metric` or strategy statements.

## Built-in functions

The full list of built-in functions and values, with parameters and units, is in the [Reference](https://chartrider.com/docs/wickscript/reference/). The most used are the moving averages `sma`, `ema`, `rma`, `wma`, `dema` and the window functions `highest`, `lowest`, `sum`, `stdev` – all `(source, length)` and returning the unit of `source` – plus `change`, `rsi`, `atr`, `mfi`, `chop`, `macd`, `stoch`, `vwap`, `abs`, `min`, `max`, `round`, `nz`, `pct`, `crossover`, `crossunder` and `isna`. Built-in values per candle include `open`, `high`, `low`, `close`, `volume`, `hl2`, `hlc3`, `ohlc4`, `truerange`, `vwap`, `obv`, `time` and `tick` (the market's price step), plus the [time, session and orderflow values](https://chartrider.com/docs/wickscript/data/).

- **Lengths** must be fixed whole numbers of at least 1: a number (`20` or `20 bars`), an input, or a variable assigned once to a constant. A length that changes per candle is an error.
- **Named arguments** are allowed for built-in functions: `stoch(14, smoothK: 3, smoothD: 3)`, `ema(source: hl2, length: 21)`.
- **Results with several parts** – `macd(…)` with `.line`, `.signal`, `.histogram` and `stoch(…)` with `.k`, `.d` – can't be shown or compared as a whole; pick a part.

```wickscript
wickscript 1
indicator "MACD and Stochastic" in pane
m = macd(close, 12, 26, 9)
s = stoch(14, 3, 3)
show m.histogram as histogram "MACD histogram"
show s.k - 50 as line "Stoch %K - 50"
```

## Inputs

`input` declares a value the user can change in the indicator settings without editing the code. Inputs are constants: their value is the same on every candle.

```text
name = input <default> ["Label"] [from <min> to <max>] [step <step>]
name = input choice "Label" of ["option 1", "option 2", …]
name = input color <color> ["Label"]
name = input timeframe <time span> ["Label"]
name = input true ["Label"]
```

- The **default** sets the unit: `input 14` is a number, `input 0.3%` a percentage, `input 2R` an R multiple, `input 5 bars` a number of candles, `input "day"` a text. The default must be a fixed value, not a calculation.
- `input 2 ticks` is a price distance: the setting counts ticks, the script calculates with count × the market's tick size. Write `from` and `to` in ticks as well (`input 2 ticks "Offset" from 1 tick to 10 ticks`).
- The **label** is the name in the settings; without one, the variable name is used.
- `from … to …` sets the range and `step` the step size, used for Step 1 / Step 2 in the strategy panel.
- `input choice` offers a list of texts; the first one is the default.
- `input timeframe` offers 1m, 5m, 15m, 1h, 4h, 1d and 1w – for `on tf` (see [Data](https://chartrider.com/docs/wickscript/data/)).
- `input true` / `input false` is a checkbox.
- Inputs go at the top level of the script, not inside `if`, `when` or functions. An input can also be written inside an expression: `ema(close, input 9 "Fast")`.

```wickscript
wickscript 1
indicator "Inputs" on chart
length = input 20 "Length" from 5 to 50 step 5
mult = input 2 "Width (ATR)" from 1 to 4 step 0.5
source = input choice "Source" of ["close", "hl2"]
tone = input color amber "Color"
showBands = input true "Show bands"
src = close
if source == "hl2" then src = hl2 end
basis = ema(src, length)
upper = na
if showBands then upper = basis + mult * atr(length) end
show basis as line "Basis" color tone
show upper as line "Upper" color tone
```

How inputs become settings and backtest parameters is described on the [Indicators](https://chartrider.com/docs/wickscript/indicators/) page.

## Output statements

### `show`

```text
show <value> as line|histogram|event "Name" [color <color>] [width <number>] [in pane]
```

`show` draws a value on every candle and makes it an output that the [Trade Setup](https://chartrider.com/docs/strategies/trade-setup/) and other scripts can read by its name.

- `as line` and `as histogram` need numbers; `as event` needs a condition and draws a dot where it is true.
- Names must be unique within the script (ignoring case).
- `show` goes at the top level, not inside `if`, `when` or functions. To show a value only on some candles, assign it inside the block and let it be `na` elsewhere.
- Where the script draws (price chart or own panel) is decided by the head line; see [Indicators](https://chartrider.com/docs/wickscript/indicators/).

```wickscript
wickscript 1
indicator "Outputs" on chart
e = ema(close, 21)
show e as line "EMA" color #3987e5 width 2
show close crosses above e as event "Cross up"
swingHigh = na
if high == highest(high, 20) then swingHigh = high end
show swingHigh as line "New 20-bar high" color green
```

### `mark`

```text
mark "Text" [above|below] [color <color>]
```

Writes a short text at the candle: `above` (the default) above its high, `below` under its low. At most 500 marks per run.

### `level`

```text
level <price> "Name" [color <color>] [until killed | until killed or <N> bars | until <N> bars | until <condition>]
```

`level` starts a horizontal line at the given price on the candle where the statement runs, usually inside a `when` block. The level stays active until:

- `until killed` – price trades through it, checked from the next candle: a level above the close of its candle is killed when a high goes above it, a level below when a low goes under it;
- `until 20 bars` – it expires after 20 candles; `until killed or 20 bars` – whichever comes first;
- `until <condition>` – it ends at the close of the candle on which the condition is true.

Without `until`, a level stays active. Active levels are drawn solid to the right edge with their name; ended levels are drawn faded and dashed. At most 500 levels per run (the oldest ended ones are dropped first). Levels draw in the price chart.

```wickscript
wickscript 1
indicator "Swing levels" on chart
when high == highest(high, 20)
    level high "Swing high" color red until killed or 100 bars
end
when low == lowest(low, 20)
    level low "Swing low" color green until killed
end
```

### `hud`

```text
hud "Text {expression} more text"
```

Adds a line to the HUD stack under the script's title. Each `{…}` holds one expression; it shows the value on the last candle, formatted by its unit (yes/no, candles as whole numbers, `%`, `R`, other numbers with 2 to 6 decimals depending on their size, `–` for `na`). Only HUD lines that ran on the last candle are shown – a `hud` inside an `if` shows only while its condition is true.

```wickscript
wickscript 1
indicator "Status" on chart
r = rsi(close, 14)
hud "RSI {r} · ATR {atr(14)} · change {pct(close, close[1])}"
if r > 70 then
    hud "Overbought for {bars since (r <= 70)} bars"
end
```

### `alert`

```text
alert "Text"
```

Raises an alert with a fixed text. In the chart, an alert on the newest live candle appears as a notification, once per candle. The text is shown as written (no `{…}` values). Put alerts inside `when … end`; outside, the checker warns that the alert would fire on every candle.

### `log`

```text
log "Text {expression}"
```

Writes a line for every candle on which the statement runs. The Scripts tab shows the lines of the candle at the crosshair and the most recent lines; the last 2,000 lines of a run are kept. Use it to debug a script.

```wickscript
wickscript 1
indicator "Debug" in pane
gap = open - close[1]
when abs(gap) > atr(14)
    log "gap {gap} at RSI {rsi(close, 14)}"
    alert "Big gap"
end
show gap as histogram "Gap"
```

Note the difference: `log "text"` writes a log line, `log(x)` is the natural logarithm.

## How a script runs

The script runs once per candle, from the oldest loaded candle to the newest, and gives the same values in the chart, the Strategy Lab, replay and backtests. In the chart, the still running candle is recalculated with every price update, so its values can change until it closes; strategies only decide on closed candles. Limits per run: 500 iterations per `repeat`, 10,000 loop steps per candle, 500 marks, 500 levels, 2,000 log lines.
