Determinism and limits
The same program on the same data gives the same output: seeds, the history the engine keeps, the price grid and the operation budget.
The engine is deterministic: a program run twice on the same bars gives the same output. These examples show the pieces of that promise that a program touches: the seed of a random number, the bounded history, the price grid and the budget of operations.
Seeded random numbers#
math.random with an explicit seed is the same on every run.
Cuprel
// math.random is deterministic: the same seed and the same call site give the same numbers on every run and every machine.
// The seed is part of the call, so changing it changes the numbers and nothing else does.
indicator "Seeded noise"
edition = 1
display = pane
input seed: int = 7, name = "Seed", min = 0
let noise = math.random(-1, 1, seed = seed)
plot(noise, name = "Noise", style = histogram, color = color.gray)
plot(tech.sma(noise, 20), name = "Average of the noise", color = color.orange)Reading earlier bars#
close[n] and a window inside the history the engine keeps.
Cuprel
// Reading earlier bars: `close[n]` is the close n bars ago, and the engine keeps a bounded history for every series.
// This compares the close with the close 50 bars ago, 100 bars ago, and the highest close of the last 200.
indicator "Look back"
edition = 1
display = pane
let change_50 = close - close[50]
let change_100 = close - close[100]
let from_peak = close - tech.highest(close, 200)
plot(change_50, name = "Change over 50 bars", color = color.blue)
plot(change_100, name = "Change over 100 bars", color = color.purple)
plot(from_peak, name = "From the 200-bar high", color = color.red)A window beyond the history#
A constant length longer than the history cap is refused before the run.
Cuprel · error E-LIMIT-HISTORY-STATIC
// Mistake: a window longer than the history the engine keeps cannot be filled, so the run is refused before it starts (E-LIMIT-HISTORY-STATIC).
indicator "Window beyond the history"
edition = 1
display = price
plot(tech.sma(close, 100000), name = "Too long")A window inside the history#
The corrected program uses a length inside the cap.
Cuprel
// The fix: a window inside the history cap. The numbers of the cap are on the determinism page.
indicator "Window inside the history"
edition = 1
display = price
plot(tech.sma(close, 200), name = "SMA 200")Stops on the price grid#
symbol.snap rounds a price to the tick grid of the symbol.
Cuprel
// Prices live on a grid of ticks. symbol.snap rounds a price to the nearest tick, so a stop or a target is a price the market can have.
strategy "Stops on the price grid"
edition = 1
display = price
setup:
capital = 10000
size = sizing.lots(0.1)
let range = tech.atr(14)
let fast = tech.ema(close, 8)
let slow = tech.ema(close, 21)
if tech.crosses_above(fast, slow)
orders.enter("Long", side.long)
orders.exit(
"Long exit",
"Long",
stop = symbol.snap(close - 2 * range),
limit = symbol.snap(close + 3 * range),
)
plot(fast, name = "Fast", color = color.green)
plot(slow, name = "Slow", color = color.red)A loop that never ends#
The per-bar operation budget stops the run.
Cuprel · error E-LIMIT-OPS
// Mistake: a loop that never ends. Every bar has a budget of operations, and a run has a cap; the engine stops the run and names the bar.
indicator "Endless loop"
edition = 1
display = pane
mut n = 0
while true
n += 1
plot(n)