vike
ReferenceSettings files

policy.toml

The hard ceilings — leverage, per-order notional, the emulated-market slippage band, halt admission, the two dead-man switches and per-venue arming — in the one settings file no environment variable can reach.

crates/vike-config/src/policy.rs is the authority for this file. Policy answers what is this deployment never allowed to exceed, which makes it the one settings type whose value must be harder to change than the rest — so it carries fewer layers than the others, and that reduction is the entire reason it exists as its own struct. It implements neither EnvOverride nor CliOverride and has no apply_env, so policy.apply_env(&env) fails to compile. settings/policy.example.toml ships every line commented out, so the file as shipped configures nothing.

The keys

max_leverage — the maximum leverage any strategy on this machine may request, >= 1.0. Defaults to DEFAULT_MAX_LEVERAGE, which is 1.0, meaning no leverage at all: a ceiling's default must be the conservative end, so a deployment that ships no policy.toml gets the safest reading. It was [risk] max_leverage in a run-profile TOML, where vike_exec::ProfileRisk::max_leverage converts it to RiskLimits::im_requirement as 1.0 / max_leverage.

max_notional_per_order — the maximum notional of any single order, in quote currency; absent means uncapped. Its field doc calls this the clearest case the whole taxonomy exists for. It replaces two environment variables, VIKE_MAX_ORDER_NOTIONAL (read by vike-desktop and vike-cli) and VIKE_TRADEHUB_MAX_ORDER_NOTIONAL, both of which are now refused at startup by vike_config::refuse_removed_env rather than ignored — see the environment reference. The example file records where it bites: the ceiling is enforced at the node, and vike-cli trade's preview guardrail only advises, saying so on the line.

market_slippage — the aggression band for an emulated market order, as a fraction (0.002 = 0.2 %). Absent means each venue keeps its own historical literal, byte-identically. Only venues with no native market order consult it, and hyperliquid is the only one on the roster; such an adapter prices a market intent as an Ioc limit at mid * (1 ± band) and a tripped stop-market at trigger * (1 ± band). The band is therefore the worst price the order is allowed to reach — not a prediction and not a fee. Hyperliquid's compiled-in value was 0.05: 5 % on every market order and every protective stop exit, which never binds on a deep BTC book and binds completely on a thin alt. Accepted values lie in MIN_MARKET_SLIPPAGE ..= MAX_MARKET_SLIPPAGE, both imported from vike_model::market_slippage rather than redefined here, and a value outside the range is rejected rather than clamped.

halt_admit — how much evidence the halt file sentinel demands before letting a submit out: "admit" (the default) or "verify", over vike_model::HaltAdmit. It is policy-class because it governs what a kill switch lets through, and because its two values differ only under an engaged halt — during an incident, the worst possible moment to discover that the environment overrode your file.

verify is weaker than the word suggests

The field doc is explicit: only a live cTrader mount implements verify, and it refuses only what a positive report from the venue's own position book proves opens risk. Every absence — never fetched, fetch failed, socket dropped since, an answer that could not be read in full or was for another account, or simply no position reported in that symbol — still admits, because halting cannot trap you outranks halting is airtight. Every other venue degrades to "admit" and says so once, at mount, naming the reason; a cTrader venue that fell back to paper says that too, because the paper client holds no book either. crates/vike-exec/src/halt.rs is the single authority. There is deliberately no third value — a "refuse" mode was the behaviour until 2026-08-06 and was removed because it disarmed the panic button in exactly the situations that halt on their own.

See halt for what an engaged halt lets out.

link_deadman_grace_ms — how long a venue's market-data link may be reported Disconnected by its own bridge before the daemon cancels that venue's resting orders and, per deadman_action, engages the halt. It is the one key in this file whose absent state is armed, at DEFAULT_LINK_DEADMAN_GRACE_MS — 120 000 ms — and 0 is the only off, which needs no warning because writing it is a decision. Legal values are 0, or MIN_LINK_DEADMAN_GRACE_MS (30 000 ms) through MAX_LINK_DEADMAN_GRACE_MS (3 600 000 ms); the floor is the slowest measured ordinary reconnect among the lanes that report a dead link, and a value outside the range is rejected by name rather than clamped. It may carry an armed default because it reads what the bridge discloses instead of counting ticks: a market that merely closed is disclosed Stale, which never counts. Where it arms is not settable here — that is a property of the venue's adapter and of the lanes the mount subscribed. On a default live mount that is binance, bybit, okx and aster, plus polymarket behind its feature; every FX and equities venue stays off deliberately, because a close is not an outage. The daemon prints one line per mounted venue at startup saying which, and why not.

deadman_timeout_ms — the other switch, on the opposite convention: how long the whole core's ingest may be silent before it cancels every resting order. Absent is off, with one warning at mount naming the key; 0 is off as a recorded decision and is the only spelling that silences that warning; a written value arms it, legal from 1 000 ms to one day. It shipped armed at 60 s for a single morning and was made opt-in the same day, because silence is also what a closed market looks like — it halted every FX and equities mount at every session close, and the one-day maximum could not be raised past an FX weekend. Arm it on a 24/7 mount only.

deadman_action — what a trip does, shared by both switches because what does a trip do is one question: "cancel_all_and_halt" (the default — pull the book and engage the halt, in-process on every engine plus the sentinel file) or "cancel_all" (pull the book, leave the trading state alone). An unknown spelling is refused at load naming the legal two, and the key is inert while the switch it would act for is not constructed. The dead-man's switches carries the mechanism, the per-venue reading and the startup lines that say which of the two, if either, is armed on your box.

[venues] — per-venue arming

One key per roster venue, paper / demo / live, defaulting to paper everywhere. crates/vike-config/src/venue_mode.rs states the property that makes this safe to introduce into a deployment already trading real money: it is a ceiling, not a selector. The effective tier is min(mode, whatever the existing mechanisms decide) — never max — so venues.bybit = "live" does not put bybit live; it declines to stop bybit going live if its credentials, its {VENUE}_MAINNET flag and its mount arm already say so. VenueMode derives Ord over variants declared in ascending risk order and VenueMode::cap is the one spelling of the fold, so an author reaching for max has to delete a method and write their own.

It is enforced. The fold sits at crates/vike-mount/src/lib.rs's make_engine_for_account — make_engine_with_legs is a one-line delegation to it — above the credential read, so a paper venue returns the paper client without loading a credential, fetching an instrument grid or opening a socket. crates/vike-config/tests/policy_is_consumed.rs's venues row names crates/vike-mount/src/arming.rs's account_ceiling, the per-account spelling of that fold, and opens that file to check it is still there. Risk and arming puts it in order against the other gates.

The first start on a box with credentials and no [venues] table

Every venue drops to paper. That is the correct ceiling and a silent one, so it is not silent: vike_mount::venue_arming_migration warns once, names this file and this key, lists the venues whose credentials would have armed, and prints the block that restores them. It warns rather than refusing because a venue connection is a capability, and a daemon that will not start cannot flatten a position either. VenuePolicy::is_declared is the fact it self-silences on — writing the table at all, even with every venue at paper, is a stated decision and is not the same as leaving it out.

Every roster venue gets a row, including ones this build cannot mount. ibkr, polymarket and fxcm sit behind Cargo features declared far above vike-config, which cannot see them; a ceiling for an unmountable venue caps at paper anyway; and a policy.toml must stay portable across boxes whose features differ, so refusing ibkr = "demo" on a build without the feature would hard-fail a reviewed file for a line that is correct. An unknown venue id is refused by name at load with the whole roster in the message, because [venues] keys are data to serde and deny_unknown_fields cannot see them.

[accounts] — per-account ceilings

A venue you hold more than one account on addresses each by a label appended to that venue's credential keys behind a double underscore. Policy's hand-written Serialize emits accounts as a sixth field — a projection of VenuePolicy's per-account ceilings, not a second copy of them: VenuePolicy is #[serde(transparent)] over its flat map and so cannot carry a second serialized field, and a table nothing serializes gets no provenance row, no vike-cli config show cell and no example line, which is the declared-but-undisclosed shape this crate exists to refuse. The default account inherits its venue's [venues] line; a labelled account must be named here to be armed, and the venue's own line still caps it. Accounts has the credential-key grammar.

crates/vike-config/src/venue_accounts.rs carries the shared-book rule, with a correction worth reading before assuming otherwise: two active accounts of one venue that resolve to the same effective trading book are reported — loudly, by name — and both are mounted anyway. The symbol is not part of the rule; long on account A and short on account B is an ordinary spread. The hazard that is real is one book reached by two engines, and vike_mount::make_engine_accounts emits one warning per pair naming the venue, both labels and the shared book.

Two tombstones, and the gate they left behind

max_total_exposure was declared here, validated by Policy::apply, accepted by PolicyPatch's deny_unknown_fields, and consumed by no code path in the workspace — an operator who set it got validation, no complaint, and no cap. rate.max_utilization was the second: it clamped a preference that was itself read by nothing. Both now refuse their key by name, and crates/vike-config/tests/policy_is_consumed.rs is the standing gate — every field of this struct must name a file that genuinely reads it, or carry a written admission that nothing does, and a claimed consumer inside vike-config itself is rejected as self-consumption. See the settings gates.

Nothing in this file is hot-appliable. crates/vike-tradehub/src/hot_reload.rs's classify answers restart-required for the policy file before its table is consulted, so no future row can make a risk ceiling hot-appliable; a policy edit lands on disk and takes effect on the next boot.

On this page