Authoring
4 offline tools — compile a Rhai strategy, list its param() knobs, read the starter templates, and see which indicators the script host actually binds.
Four tools for writing a strategy, and none of them touches a server: they run entirely inside
the vike-cli process, so they work with no --addr, no --node and no keys. Two compile a
script (validate_strategy, discover_params), two answer "what may I write" (list_templates,
list_indicators).
Strategies are Rhai scripts. Tunable knobs are declared with
param(name, default) at the top level; the trading logic lives in fn on_bar().
validate_strategy
Compile a Rhai strategy — compile is validation. The check runs through
crates/vike-script/src/strategy.rs's discover_params, which compiles the script and runs its
top level exactly once, with no broker attached. That is the same error surface you get from
mounting the strategy for real: the daemon mounts through RhaiStrategy::compile_with_params
(resolve_script in crates/vike-tradehub/src/config.rs is the call site), and that performs the
same one-time top-level run.
discover_params is a public entry point, so the same broker-free check is callable from your
own Rust code and not only through this tool.
| Parameter | Type | Required | Notes |
|---|---|---|---|
script | string | yes | Rhai strategy source |
Returns ok: true, or ok: false with the compiler's error string. A bad script is an
answer, not a tool failure — the agent reads the error and fixes the script. Only a missing
script argument is an isError result.
Does this strategy compile?
discover_params
Read back the tunable param(name, default) knobs a script declares — the exact keys a profile's
[strategy.params] or a [sweep] table can override, in declaration order.
| Parameter | Type | Required | Notes |
|---|---|---|---|
script | string | yes | Rhai strategy source |
Returns params, one row per knob: name and its default.
Note the deliberate asymmetry with the tool above: both call the same compile path, but a compile
error here is a tool error (rhai compile error: …), because there are no parameters to report.
Call it on a script you just wrote, not only on one you were handed. validate_strategy answers
ok: true and says nothing about params, and this tool runs the top level only — so a param()
reached from inside on_bar() is reported by neither, which makes it exactly as invisible to the
author as to a reader. The tool's own description says so now (tools_spec in
crates/vike-cli/src/cmd/mcp.rs), because an agent that skipped this step and answered from its
own authorship is what the change was measured against.
What knobs does this strategy expose, and what are their defaults?
list_templates
The starter strategies, as {name, code} rows: SMA cross, RSI reversion and Donchian
breakout. Each is parameterized via param(), so a template drops straight into run_sweep
without editing. The sources are static data in crates/vike-cli/src/cmd/mcp.rs's TEMPLATES,
copied verbatim from vike_studio_core::templates.
That is a hand copy, not a shared import, and there is no cross-crate drift gate — the two copies must be edited together. They are identical today, and nothing but that convention keeps them so; each side carries its own execution gate over its own copy instead.
This tool takes no arguments.
Two tests stand behind the list, and the second is the one that matters: one compiles every
template, and every_shipped_template_reaches_the_broker runs each one over 80 bars against a
broker double and asserts an order actually arrives. Compiling is the wrong bar for a surface an
agent copies from — a strategy whose every call sits inside fn on_bar() compiles perfectly and
can still fail on every single bar (see the callout below).
Read the Donchian starter's body before trusting its name
It stands in with sma, symmetrically, because a bare donchian(len) is not bound. bare_name_binds
in crates/vike-script/src/engine.rs binds a bare name when the indicator has one output or when its
first line is the namesake, and donchian's first line is upper. Its per-line accessors —
donchian_upper(), donchian_mid(), donchian_lower() — are all callable, so the bare call is a
function-not-found on the first bar rather than a silent upper-band read. The template's own comment
says "multi-output indicators are not bound", which is wider than the rule the host applies.
Show me the vike strategy templates and explain what each one trades.
list_indicators
The host-bound callable set — what the Rhai engine actually registers, joined onto the
indicator catalogue's own metadata (vike_indicators::registry()). This is deliberately not the
whole catalogue: it is filtered to what a script can really call.
That question is vike_script::is_callable, and it has two arms — an indicator qualifies under
its bare name or through at least one per-line accessor. bollinger has no bare call and three
accessors, so it is listed and you reach it as bollinger_mid(20).
The filter and the rows both live in crates/vike-cli/src/cmd/indicators.rs, which also feeds the
human vike-cli indicators listing, so the two surfaces cannot disagree about the built-in
catalogue. An indicator installed from user_data/indicators shows up in that listing and in no
answer this tool gives.
The answer is tiered, because the bound set is now most of the catalogue and a full dump would spend an agent's context on indicators it will never use.
| Parameter | Type | Default | Notes |
|---|---|---|---|
name | string | omit | One indicator in full: its parameters with defaults and the value it returns |
category | string | omit | One family in full; matched case-insensitively |
| (neither) | — | — | The compact roster: name + category per indicator |
Returns indicators plus a count. The compact roster also carries a note explaining how
to pull detail. A name or category nobody can serve is an isError result, not an empty list
— an empty answer reads as "that family is empty in this build" and sends the reader hunting
through a config. For a catalogued indicator the host deliberately holds back, the error quotes
the host's own reason (vike_script::unbound_reason) verbatim rather than paraphrasing it. That
arm fires only for a name callable under NO spelling — a Rhai reserved word, a host-function
collision, a batch-only indicator, or one with more parameters than the host binds. A name whose
bare call is refused but whose accessors work, like bollinger, never reaches it: it comes back
as an ordinary catalogue row.
An unbound name compiles and then fails on every bar
Rhai resolves a registered function when the line runs, not at compile time, and the compile
path only runs a script's top level. So a misspelled indicator or a wrong arity compiles cleanly,
then raises ErrorFunctionNotFound on every bar. Argument type is the exception:
register_arity_forms binds every arity with rhai::Dynamic parameters, so sma(5.0) resolves
just like sma(5), and only a non-numeric argument (sma("20")) fails — as a named runtime error
rather than function-not-found.
crates/vike-script/src/strategy.rs's RhaiStrategy swallows each one fail-safe (zero orders
that bar) and, after 10 consecutive errors, disables the strategy outright. It looks mounted and
never trades. Check names against this tool, not against memory.
Which indicators can a vike Rhai strategy call, and what parameters does
rsitake?
node_snapshot
The one read tool against a running vike-tradehub node — orders, positions, per-venue equity, recent events — and the tool that settles an ambiguous write.
Backtesting
5 tools over the datahub — backtest, parameter sweep and anchored walk-forward from a profile TOML, plus the strategy roster and the stored-series inventory.