vike
Scripting

Scripting

A Rhai script as a real strategy in Vike Trader — the two-part script contract, the user-written indicator seam beside it, where the files live, and what mounts a script live.

A Rhai script in Vike Trader is not a sandboxed toy beside the real strategies; it is one. crates/vike-script/src/lib.rs describes the crate as running a Rhai script as a vike_model::Strategy<B> — the same seam the compiled Rust strategies use, at a millisecond cadence — and every engine that drives strategies, backtest and live alike, drives a script the same way. This tree covers that crate and one more: the script engine is vike-script, while the user-owned strategy files and their presets live in vike-studio-core/src/user_strategies/. They are documented together because the reader is the person writing a strategy, whichever crate ends up loading it.

The contract, in two halves

The lib.rs header states the script contract as two halves, both about calling an indicator.

What a script may call. RHAI_INDICATORS is the host-bound indicator set, derived from vike_indicators::registry() rather than hand-listed: almost the whole catalog, minus the entries unbound_reason rejects because a scalar binding of them would return a silently wrong number. Each bound indicator has one form per argument count, from zero (every registry default) up to its own parameter count, and unbound_reason says why a missing one is missing. The engine walks the rules; the cheatsheet is the scannable version.

How it must call it. An indicator function must be called unconditionally on every on_bar invocation to keep its streaming state correct. The header is blunt about the alternative: an indicator referenced behind an if silently skips whichever bars it was not called on, desyncing it from the bar series. That is a consequence of the fed-once-per-bar cache in engine.rs's indicator_value, and it is the one rule in this tree that fails without an error.

Defining an indicator, not just calling one

The other seam is RhaiIndicator in indicator.rs. A file under user_data/indicators/<name>.rhai compiles to a real vike_indicators::Indicator — fed-once-per-bar state, vectorize parity by construction, and no access to the order verbs — and RhaiStrategy::compile_with_indicators binds it beside the built-ins. The two halves above then apply to it unchanged, including multi-output: a file declaring fn outputs() { ["upper", "mid", "lower"] } gets its lines through the same line_fn_name the built-ins use (user_line_accessors is the line_accessors twin), under the same rule for whether the bare name binds at all. A file that declares no outputs() is single-output and unchanged.

Because it is a real Indicator, the same compiled prototype also plots: user_meta wraps one as a &'static vike_indicators::IndicatorMeta whose factory clones it, which is what the chart's ƒx picker mounts. Nothing about the script contract changes for that — a file written for a strategy is already a chart study — except one optional addition, fn overlay(), the author's answer to "price pane, or my own?". Writing an indicator is the contract; Loading indicators is how the folder reaches a process.

Where a script runs

A strategy script is compiled in three contexts — the backtest harness, the live daemon and offline validation — and they reach the same engine.

  • Backtest. The harness registry's rhai arm in crates/vike-backtest/src/harness/registry.rs's strategy_by_name compiles an inline src param into a RhaiStrategy<SimBroker>; vike-cli backtest --script is what puts a file's source there. See the harness registry.
  • Live. Rhai scripts are live-mountable on vike-tradehub, named explicitly by path in the reviewable profile. crates/vike-tradehub/src/config.rs's header spells it — [strategy] rhai = "<path>", "mutually exclusive with name", "the script named EXPLICITLY by path in this reviewable file, its compiled source's sha256 logged at INFO as the audit trail" — so nothing is auto-discovered, and name = "rhai" is refused with a message pointing at the path spelling. Four rails hold that mount, and the fourth is the one this tree leans on: crates/vike-tradehub/Cargo.toml's vike-script dependency comment records that the daemon compiles through "the SAME resource-limited engine the backtest arm builds (vike-script's one build_engine — this daemon registers no host function of its own)", so the host functions a script may call live are the backtest's. The one gap is not the engine's but the process's: the user-indicator set build_engine binds is process-wide and installed by the BINARY, and this daemon installs none (the four that do are on Loading indicators), so a user_data/indicators/ file a vike-cli backtest script can call is a function-not-found here. The same comment calls a scripting engine inside the binary that signs real orders a supply-chain decision made deliberately, and names the Python/JS refusal beside it — a refusal by class, a product statement rather than a deferral.
  • Validation. vike-cli mcp's validate_strategy tool compiles a script through discover_params, which the tool's own doc calls the same error surface as mounting it.

A script cannot import, and cannot print to stdout

build_engine installs a DummyModuleResolver, so import "…" as m; is refused: a default Rhai engine resolves imports from the process's working directory, which for the daemon is the project holding the credential store. redirect_script_output points print/debug at the tracing facade instead of the host's stdout, because two hosts — vike-cli mcp and vike-tradehub — use stdout as a protocol. Both are argued in The engine.

Where the files live

A user's strategies are files, not rows in a blob — crates/vike-studio-core/src/user_strategies/mod.rs opens with exactly that sentence, and its layout is one folder per strategy under user_data/strategies/rhai/<name>/<name>.rhai, presets as .toml beside the entry, and strategies/rust/<builtin>/ holding presets for a strategy whose code is in the binary. User strategies is the directory scan and its named diagnostics; Presets is how <strategy>/<preset>.toml becomes the params a strategy is constructed with; Migration is the one-time move off the Studio's studio_strategies.json blob.

Where a script is written

The Studio's editor is where a strategy script is edited in the app. The other interpreted tier in the product — a Rhai study — is a different thing on a different engine: crates/vike-studio-core/src/rhai_study/bind.rs registers a store-reading surface for research, not the order verbs, and its operation ceiling is set for a whole window rather than a bar. A strategy trades; a study answers a question about data.

The pages

pagewhat it is
The enginethe resource-limited rhai::Engine, what is registered on it, and what a script may not do
The order vocabularythe Intent verbs and the ScriptCtx they are recorded into
Writing a strategythe compiled-script strategy: hooks, param(), the one-time top level, fail-safe
Writing an indicatora Rhai indicator as a real Indicator
Loading indicatorsuser_data/indicators/*.rhai, the report, and which binaries install it
User strategiesthe strategy tree and its diagnostics
Presets<strategy>/<preset>.toml to constructed params
Migrationoff the JSON blob, once
Limitsthe ceilings the engine enforces and what hitting one looks like
Cheatsheetevery host function and verb on one page

On this page