vike
Desktop appStudio

Remote runs

The Backend toggle sends a Run, Sweep or Walk-Forward to a vike-datahub server over TCP and folds the answer into the same results pane, with the one refusal the split-plane rule adds.

Every Run, Sweep and Walk-Forward in the Studio has a Backend: where it executes. The toggle sits on the toolbar's second line. Local, the default, runs in-process over the Studio's own store — crates/vike-studio/src/remote.rs's Backend doc calls it byte-identical to the Studio before the backend existed. Remote offloads the run over TCP to a vike-datahub server — the compute-to-data backtest service, where the engine runs next to the data and only the answer crosses the wire — with an address field beside it (host:port) that starts at DEFAULT_REMOTE_ADDR, 127.0.0.1:7878: the localhost port an SSH-tunnelled data service serves on, and vike-cli's own default. The choice is a runtime toggle held on the state struct, not persisted to disk.

The contract: a drop-in for the local path

The module header states it. Each spawn_*_remote returns the exact same receiver type as its vike_studio_core::spawn_* twin, so StudioState::poll folds a remote outcome with zero changes and the results pane renders it unchanged. The wire answer is mapped back onto the same BacktestResult, StudioSweep and WalkForwardReport the local engine produces, and every failure — a connect fault, a server-side error, a protocol desync — arrives as an Err(RunError) on that receiver, never a panic.

The strategy and slice go out as DTOs, defined once, in the light vike-datahub-client crate (wire_studio), and the server — vike-datahub behind its serve-datafusion feature — converts wire to engine and runs the same vike_studio_core::run::run_slice the local path runs. A native strategy's parameters ride as TOML text, the exact text the server parses back with StrategySpec::native_from_toml_str, so a local and a remote native run resolve bit-identical params. Coming back, to_backtest_result fills the rendered fields and leaves the rest at their default; each closed trade is reconstructed losslessly; a sweep's dsr or pbo the server could not assess arrives as NaN, the sentinel the results pane already handles; the walk-forward report is a full mirror.

Engine parameters are the defaults, on both backends

run_slice_remote's doc: params go out as None, and the server resolves every engine field from EngineParams::default() — matching the local path, which uses the same default. The protocol carries an optional cost and cash override; the Studio does not expose one yet.

Errors render like local ones

The server stringifies a run failure kind-first ("{kind}: {message}"), and parse_wire_run_error recovers the prefix — compile, data, strategy — and rebuilds the matching RunError, so a remote failure renders identically to the same failure run locally. A connect failure or a protocol-version mismatch becomes a data error naming the address, shown in the Studio's existing error banner; the mismatch is legible because DatahubClient::connect performs a Hello/Welcome handshake and, when the versions disagree, fails naming both.

A keyed data service refuses the Studio's dial

The Studio dials with DatahubClient::connect, the unauthenticated constructor — its doc names the Studio's Backend::Remote among the callers it keeps working untouched against a key-less server. Against a server that requires authentication it fails at the handshake with a message naming VIKE_DATAHUB_OBSERVE_KEY and VIKE_DATAHUB_CONTROL_KEY; the Run* verbs sit in the control scope, because they compile client-supplied Rhai on the server.

The store and the backend are two different things

The backend is where compute runs. The store is where the Studio reads history from, and it can be remote on its own: StudioState::store is the trait handle, either a local store or an RPC-backed RemoteHistStore whose reads go over the same protocol — read-only, one fresh connection per read. The desktop app decides which in open_studio_store, on the resolved datahub address: the explicit config.datahub_addr key (see config.toml), else the address the connected backend's Welcome advertises. With an address it opens a remote store; without one there is no local store to open. The binary also sets store_is_remote and seeds the backend after construction. There is no local engine at all: the Studio is remote-only, and with no address its open fails naming both fixes — set config.datahub_addr, or connect a backend whose daemon advertises a datahub.

Connect first, then open the Studio

open_studio_store's doc calls this the contract's sharp edge: the store is opened once and kept for the session. A Studio opened before any backend connects resolves from the explicit key alone and never picks up the advertisement. Every other datahub consumer re-resolves per read.

The one refusal: tick slices over a remote store

remote_store_tick_refusal is the split-plane rule: with a remote store, a tick slice runs only on the Remote backend. A local tick replay would need the tick tape — scan_book_updates, scan_depth — pulled over the wire, which is exactly what compute-to-data refuses; the RPC store keeps both verbs as read-only errors. Instead of dispatching, the Studio shows one line: switch Backend to Remote. The guard sits in front of Run, Sweep and Walk-Forward alike, and never fires for a local store, a bar slice (its load_bars read is a served verb — small, not the tape), or the Remote backend itself.

The same offload from a terminal is vike-cli backtest, sweep and walkforward — Running a backtest covers the verb, and Run a sweep on the data service walks one through.

On this page