vike
ReferenceBinaries

vike-tradehub

The unattended live trading daemon — a TOML profile, newline-JSON stdio, an optional authenticated node server, and a bounded teardown.

The headless trading daemon. It runs a strategy on a server with no GUI, controlled by a TOML profile plus newline-JSON vike_exec::Commands on stdin, emits a periodic one-line JSON snapshot summary on stdout, and survives an SSH disconnect — it is a systemd service, detached from the login session (crates/vike-tradehub/src/tradehub_cli.rs; deploy/vike-tradehub.service is the shipped unit).

usage: vike-tradehub --config <profile.toml> [--profile <run_profile.toml>]

  --config PATH   the daemon profile TOML (venue / token_id / A-S mount shape) — REQUIRED
  --profile PATH  the operator-budget RunProfile TOML ([risk] ceilings); absent, $VIKE_RUN_PROFILE
  -h, --help      print this and exit 0
  -V, --version   print the version and exit 0

The bin is a thin shim over vike_tradehub::tradehub_cli::run; it is the composition root and holds what a library may not — the one std::env::vars() sweep and the one current_dir(), both handed to run (crates/vike-tradehub/src/main.rs).

Paper is the default; live is a flag

By default the daemon mounts the resolved strategy on the production live core with the paper exchange as its ExecutionClient — through vike_run::build_paper_strategy_core_with, so there is zero real-money, credential or geo risk and no live feed. The tradehub_live flag (<project>/settings/flags.toml, still overridden by VIKE_TRADEHUB_LIVE) instead mounts the same strategy on the real vike_run::build_node core, with per-venue credential-gated exec and the venue's own live feed. Off is byte-identical to the pure-paper daemon.

The live path is defended by five gates and fails loud, never soft: the master flag; per-venue credentials (absent creds keep that venue paper); the venue's own second gate (HYPERLIQUID_MAINNET); crates/vike-tradehub/src/config.rs's LIVE_WIRED_VENUES, which hard-errors on an unwired venue rather than falling back to paper silently; and the profile's symbol checked against vike_run::WIRED_MARKETS by DaemonProfile::validate_for_live, because make_engine wires no extra symbols and a foreign symbol on a wired venue would have its orders and fills dropped at accepts_symbol.

No policy.toml means an all-paper daemon

policy.venues is the per-venue arming ceiling, read at the top of vike_mount::make_engine above the credential read, and its default is paper for every venue. A box with no policy.toml mounts all paper, whatever the credential store holds. vike_mount::venue_arming_migration warns once at startup, naming the file, the [venues] key and every venue it refused. See policy.toml.

Which strategy is a profile field

[strategy] name = "…" plus [strategy.params] — deliberately the same table vike_backtest::harness::BacktestProfile has — resolves through the shared registry vike_strategy::strategy_by_name, so the profile that was backtested is the profile that trades. Absent [strategy], the mount is the Avellaneda–Stoikov vike_mm::SpreadMaker built from the profile's own maker fields by vike_run::build_maker. A Rhai script mounts by path ([strategy] rhai = "<path>", mutually exclusive with name), and the daemon logs the source's sha256 and path at INFO — the journal's answer to which code traded.

DaemonProfile::validate refuses, at profile load, a name that is unknown, simulator-only (vike_strategy::SIMULATOR_ONLY), or one that would resolve and then never trade (vike_strategy::LIVE_CAPABLE carries a written reason for every "no"). Four further refusals cover [strategy.params], each the case the previous one passes: unknown_params (a key no reader reads), mistyped_params (a value no reader can take), misrouted_params (a key the mount overrides — including the ones naming which instrument and which venue real orders go to), and unarmable_params (a table that passes all three and still describes no order at all). The mount then echoes the resolved configuration through DaemonProfile::effective_params.

[[mounts]] is an array of the single-mount blocks, so strategies sharing a venue account share a process; each row mounts under a derived controller id (DaemonProfile::derived_controller_id), and two rows deriving one id are refused at load.

Control surfaces

With config.tradehub_addr (or VIKE_TRADEHUB_ADDR) set and a VIKE_TRADEHUB_OBSERVE_KEY in the credential store — absent either, start_observe_server starts nothing and the daemon keeps trading headless — the daemon also starts an authenticated node server so a laptop GUI can watch this node's snapshots — see observe mode and the node protocol — and, behind the tradehub_control flag plus a VIKE_TRADEHUB_CONTROL_KEY, trade it. crates/vike-tradehub/src/server.rs's accept_command is the one acceptance path, shared by every control surface, so the rate token, the notional cap, the rationale sanitizer, the lowering, the single-writer lane and the audit record are identical by construction rather than by review. A remote submit must carry a pre-minted client_order_id or it is refused: a remote peer whose id the runtime minted has no stable handle to cancel or dedup by. The optional rationale is remote free text landing verbatim in a structured JSON log line, so crates/vike-tradehub/src/audit.rs's sanitize_reason is mandatory at the call site.

Reachability is the outer barrier

Everything in server.rs is authorization — none of it is confidentiality or integrity. The handshake is plaintext and authenticates the connection, not each frame. There is deliberately no TLS: the listener stays on loopback and an SSH tunnel supplies both properties. Three things hold that up, each stating its own reasoning — bind_exposure (a non-loopback bind is refused unless explicitly opted in), MAX_CONNECTIONS and HANDSHAKE_MAX_FRAME_LEN.

The publisher (crates/vike-tradehub/src/publish.rs) reads only the coalesced arc-swap snapshot cell, on its own thread, and pushes shared frame bytes into each subscriber's bounded, drop-oldest mailbox — it must never enter the core fold. The opt-in Telegram channel is a second remote write surface behind an off-by-default Cargo feature and four runtime gates; it shares accept_command with the TCP path.

Lifecycle and stop

vike_ops::stop::install_handlers is the first statement of run, above argv and the settings load (crates/vike-tradehub/src/tradehub_cli.rs's run). The gate behind it is narrower than that: crates/vike-ops/tests/graceful_stop_pin.rs's the_tradehub_handler_is_installed_before_anything_can_place_an_order checks by position only that the install precedes every mount call, because between a mount returning and the handler installing, the maker may already hold resting orders at the venue while SIGTERM still carries the OS default disposition. One stop flag has many triggers: the shutdown/quit word on stdin, Ctrl-D on a TTY, SIGTERM/SIGINT, the Windows console control events, and on Windows the appearance of <state>/ plus vike_ops::stop::STOP_FILE_NAME. A non-TTY EOF does not stop the daemon — it keeps trading headless. The teardown is bounded by vike_ops::shutdown::run_with_deadline wrapping vike_core::CoreHandle::shutdown_and_join.

stdout is protocol-only — newline-JSON summaries and acks; every log and diagnostic rides the vike_log file/stderr layer. resolve_settings runs before the log subscriber is built, because the log directory and both log levels are themselves settings; it returns its warnings as data for main to emit once a subscriber exists. A removed environment variable refuses startup outright.

Reconciliation defaults to quarantine here rather than to the workspace default: under hybrid a freshly restarted daemon would auto-apply position drift, rewriting position size and booking realized PnL at the venue's price before anyone has confirmed the venue's report is complete. Its health gate is fed by the feeds this mount actually wired (recon_feed_statuses_of): a CEX venue contributes its own status handle and is gated on it, while every venue that contributes none — hyperliquid and polymarket among them — reads Healthy. Alerting rides the periodic summary tick off-fold, and the rules file is the gate. record-feeds and materialize are the crate's two DataFusion arms, both off by default to keep the daemon DataFusion-free.

On this page