vike
Scripting

Cheatsheet

Every host function a strategy script can call, every hook it can define, and the indicator call forms — one scannable page beside the full ones.

Everything below is registered by crates/vike-script/src/engine.rs's build_engine onto a resource-limited engine (the numbers are on Limits) and driven by crates/vike-script/src/strategy.rs's RhaiStrategy. The non-indicator names are pinned in engine.rs's HOST_FN_NAMES. The indicator names are derived from the registry at run time and are deliberately not listed here — vike-cli indicators prints the callable set (indicators).

Hooks

hookcallednotes
fn on_start()before the first barposition()/price() are not refreshed here — there is no symbol yet, so they read their initial 0.0
fn on_bar()once per barthe only place to read the bar and trade
fn on_stop()when the engine stops the strategy

A hook is detected by name as a zero-argument fn; one the script never wrote is skipped, never attempted. There is no fill hook — a script observes its resulting position through position(). The top level (everything outside a fn) runs EXACTLY ONCE at compile: a top-level const is visible inside every hook, a top-level buy(1.0); is discarded, and a top-level indicator reference is cleared so the first real bar is the first bar any indicator sees. A hook's own let locals live for that one call; the script keeps no cross-bar mutable state of its own.

Context — the current bar

functionreturns
close() open() high() low() volume()f64, that field of the current bar
index()i64, the current step index — bars processed so far minus one
now()i64, engine time in epoch milliseconds

All snapshotted before the hook runs (register_reads).

Account

functionreturns
position()f64, signed position size in the mounted symbol; 0.0 when flat
price()f64, the last mark — the engine's post-fill price in a backtest, the bar close live
equity()f64, account equity now

The definitions are vike_model::Broker's own (crates/vike-model/src/strategy/mod.rs); the script reads a snapshot of them.

Orders

verbmeaning
buy(qty)market, side +1
sell(qty)market, side -1
market(side, qty)market; side is 1 to buy, -1 to sell
limit(side, qty, price)limit at price

qty is f64 in units; side is an integer. A verb records an Intent (crates/vike-script/src/ctx.rs); after the hook returns cleanly the intents are drained onto the broker in order, on the mounted symbol. A hook that errors drains nothing. The full page is The order vocabulary.

Parameters

let fast = param("fast", 5.0);   // top level, so the one-time run bakes the value in

param(name, default) -> f64. The default may be written 5 or 5.0 — it is Dynamic, not f64, after a version that rejected the integer spelling. First-seen wins in the (name, default) list discovery reports; the call itself still returns its own default when nothing overrides it. A sweep injects overrides BEFORE the one-time top-level run, so a swept value holds for the strategy's whole life; discover_params lists the (name, default) pairs in declaration order for the sweep grid.

Indicators

formexamplerule
name()psar()every parameter at its registry default — the only spelling for a parameterless indicator, and the only way to reach a fractional default
name(p1) … name(p1, p2, p3)sma(20), rsi(14)up to the indicator's own parameter count, at most 3; parameters left off the end take their defaults; one argument too many is a function-not-found
name_line(...)bollinger_mid(20, 2.0), macd_signal(12, 26, 9), stochastic_k(14)one accessor per line of a multi-output indicator, same arguments as the bare name; all lines of one indicator share one streaming instance

Arguments are int or float; a non-numeric argument is a runtime error, not NaN. A bare name binds when the indicator is single-output or its line 0 is the namesake line (macd(), adx()); bollinger, donchian, keltner, envelopes and std_error_bands are refused bare because their line 0 is upper — spell the line, bollinger_mid(). Never bound in any spelling: ichimoku, zigzag and williams_fractal (they read future bars), kst (nine parameters), and var (a Rhai reserved word — use stddev).

Call every indicator unconditionally, every bar

An indicator is fed the current bar the first time it is referenced in that bar; a second reference returns the cached value. One behind an if silently skips the bars it is not called on and desyncs from the series (crates/vike-script/src/lib.rs). NaN means warming up — the shipped templates open with if s.is_nan() { return; }.

A user indicator from user_data/indicators/<name>.rhai is called the same way: my_ema(), my_ema(20) for each param() it declares, my_bands_mid() for a declared line. Writing an indicator has the file contract; in short, fn on_bar(bar) is required, bar is a map of ts/open/high/low/close/volume, this is the state fn init() built, returning () means warm-up, and param() is the only host function that engine registers — an indicator cannot trade.

Diagnostics

print(...) and debug(...) go to the tracing target vike_script at INFO and DEBUG, never to the host's stdout (redirect_script_output). import is refused. A runtime error costs that bar's orders; ten in a row disable the strategy.

A whole strategy

The shipped SMA cross template (crates/vike-script/src/templates.rs's SMA_CROSS), which also uses Rhai's own to_int, is_nan and abs:

let fast = param("fast", 5.0);
let slow = param("slow", 20.0);
fn on_bar() {
    let f = sma(fast.to_int()); let s = sma(slow.to_int());
    if s.is_nan() { return; }
    let target = if f > s { 1.0 } else { -1.0 };
    let delta = target - position();
    if abs(delta) > 1e-12 { market(if delta > 0.0 { 1 } else { -1 }, abs(delta)); }
}

On this page