millionchart
فارسی

Already write scripts elsewhere?

If you already write scripts for charts in another language, this page maps the ideas you know to how they are written here, and lists the real differences.

If you have written indicators or strategies in other chart scripting languages, most of the ideas carry over: a script runs once for each bar, reads price series, keeps some values from bar to bar and draws what it works out. This page names each idea, shows how Cuprel writes it and links to the page that teaches it. It does not compare products; it only helps you find the words. If you have never written a script, start with Lesson 1: Your first script instead.

Idea by idea#

The ideaHow it is written hereRead
A header that says what kind of script it isindicator "Name", strategy "Name" or module Name, with its settings indented under itSyntax and layout
A setting the user can changeinput length: int = 20, name = "Length", min = 1Inputs
Price series and earlier barsopen, high, low, close, volume; close[1] is one bar backValues and types
Bar-by-bar execution and repaintthe script runs once per bar, oldest to newest; a forming bar changes until it closesLesson 16: Results that look too good
Lines and coloursplot(x, name = …, color = …, width = …, style = …), level, shade, chart.backgroundPlots and drawings
Labels, lines, boxes and tableslabel.new, line.new, box.new, table.newLesson 8: Labels, lines and boxes
Conditions and missing valuesif, if … then … else, match; na, is_na(x), x ?? 0na, errors and arithmetic
Functions and loopsfn name(…): type, for … in, whileFunctions, types and enums
A value kept from bar to barstate (and state day, which starts again each trading day); mut is recomputed each barlet, mut and state
Another timeframedata.series("240", expression, symbol = symbol.id), the last closed bar by defaultLesson 10: Other timeframes and symbols
Another symbolnot available in this release: the script is refused (E-REQ-UNDECLARED) or stops (E4001)Lesson 10: Other timeframes and symbols
Alerts from a scriptalert.when and alert.send compile and run, but alerts on your own scripts are switched off in this releaseLesson 11: Alerts that say something
Strategies and testing themstrategy "Name", orders.enter / orders.close / orders.exit, costs in setup: and execution:; tested in the Strategy LabLesson 15: Testing in the Strategy Lab

The real differences#

These are the points to read twice, because habits from other languages may lead you astray here.

  • Editions. Write edition = 1 in the header. The edition says which reading of the language the script was written for, so a script keeps its meaning when a later edition exists.
  • Blocks by indentation. A block is the lines indented under it, four spaces a level. There are no braces and no end words, and each statement has a line of its own.
  • The same numbers on every device. The engine computes the mathematical functions with one fixed implementation, so a script on the same bars gives the same result on every supported device. Determinism and limits says exactly what this covers.
  • Budgets. Each bar may spend at most 500,000 operations and a run 600,000,000; history, drawings, requests and memory have limits too. A script that spends more than its budget is stopped with a message that names the bar. See Limits and budgets.
  • na rules. Comparing a value with na is never true, not even na == na; the checker warns (W-NA-COMPARE). Ask is_na(x), or replace a missing value with x ?? 0.
  • Lazy operators. The right side of and, or and ??, and the branches of a one-line if … then … else, run only when they are needed. If you are used to a language where both sides always run, note that a call that remembers earlier bars (such as tech.sma) may not stand there: the checker refuses it with E3302. Write it on a line of its own first:
Cuprel · error E3302
indicator "A call that remembers, in an operand"
    edition = 1
    display = price

let strong = close > open and close > tech.sma(close, 20)
plot(if strong then close else na, name = "Strong bars", style = circles)
Cuprel
indicator "Bound on its own line first"
    edition = 1
    display = price

let average = tech.sma(close, 20)
let strong = close > open and close > average
plot(if strong then close else na, name = "Strong bars", style = circles)
  • Private scripts. Every script belongs to your account. There is no way to share or publish one in this release.
  • One layout. The editor's Format button (and Format when saving) rewrites a script in the one standard layout this guide uses, so you do not need to learn a style guide.

Lazy operators, E3302 and W3302 and Calls that keep a state explain the last rule in full.

The same idea, written in Cuprel#

Each program is complete: paste it into a new script, press Save and Add to chart.

A moving average with two settings:

Cuprel
indicator "Average with a setting"
    edition = 1
    display = price

input length: int = 20, name = "Length", min = 1
input src: source = close, name = "Source"
plot(tech.sma(src, length), name = "Average", color = color.orange, width = 2)

Marks where a fast average crosses a slow one. The crossing tests are worked out on lines of their own:

Cuprel
indicator "Crossing marks"
    edition = 1
    display = price

let fast = tech.ema(close, 10)
let slow = tech.ema(close, 30)
let up = tech.crosses_above(fast, slow)
let down = tech.crosses_below(fast, slow)
plot(fast, name = "Fast", color = color.green)
plot(slow, name = "Slow", color = color.red)
mark.shape(up, name = "Up", shape = triangle_up, place = below, color = color.green)
mark.shape(down, name = "Down", shape = triangle_down, place = above, color = color.red)

A value kept from bar to bar with state:

Cuprel
indicator "Bars since a new high"
    edition = 1
    display = pane

state count = 0
if close >= tech.highest(close, 20)
    count = 0
else
    count += 1
plot(count, name = "Bars since", style = histogram, color = color.teal)

A value from the four-hour chart. By default it is the value of the last four-hour bar that has closed, so it does not change while that bar is still forming:

Cuprel
indicator "Four-hour average"
    edition = 1
    display = price

let h4 = data.series("240", tech.ema(close, 20), symbol = symbol.id)
plot(h4, name = "4h EMA (last closed bar)", color = color.orange, width = 2)

Missing values: ?? gives a replacement, is_na asks whether a value is missing:

Cuprel
indicator "Missing values"
    edition = 1
    display = pane

let change = close - close[1]
let average = tech.sma(change, 50)
plot(average ?? 0, name = "Average change, 0 while unknown", color = color.blue)
plot(if is_na(average) then 1 else 0, name = "Still unknown", style = columns, color = color.gray)

A function that gives back two results at once:

Cuprel
indicator "Range of the last bars"
    edition = 1
    display = price

fn range_of(length: int): (float, float)
    return (tech.highest(high, length), tech.lowest(low, length))
let (top, bottom) = range_of(20)
plot(top, name = "Top", color = color.green)
plot(bottom, name = "Bottom", color = color.red)

A small strategy with a size and a commission. The chart marks its trades; the Strategy Lab tests it over a longer history:

Cuprel
strategy "Crossing strategy"
    edition = 1
    display = price

setup:
    size = sizing.lots(0.1)
    fees = fee.per_lot(3.5)

let fast = tech.ema(close, 10)
let slow = tech.ema(close, 30)
if tech.crosses_above(fast, slow)
    orders.enter("Long", side.long)
if tech.crosses_below(fast, slow)
    orders.close("Long")
plot(fast, name = "Fast", color = color.green)
plot(slow, name = "Slow", color = color.red)

Next: Lesson 1: Your first script