vike
Tutorials

The grid template on paper

Mount the grid strategy on the production live core with the paper exchange behind it, and understand every refusal the profile has to pass first.

grid is a two-sided ladder maker, and it is a good first mount because everything it does is visible in a few minutes of bars: it rests limits, they fill, it takes profit one step back, and it re-arms. Mounting it on paper puts it on the PRODUCTION live core — the single-writer runtime, the ingest lanes, the RiskGate, the one live order path — with vike_paper::PaperExecutionClient standing in for a venue, so there is no real money, no credential and no geo risk anywhere in the run.

What the strategy does

crates/vike-strategy/src/grid_dca.rs's Grid rests rungs buy limits BELOW and rungs sell limits ABOVE an anchor, spaced by step, size units each — the sell side is an opening short when flat. Each rung is an oscillator: when its ENTRY limit fills, the strategy places a take-profit limit one step back toward the anchor; when THAT fills — a completed round trip — the entry re-arms. Inventory is therefore bounded by rungs · size per side.

A HARD STOP fires when price leaves [anchor − band, anchor + band]: the strategy flattens the net position and HALTS — no re-arms, no new legs.

The knobs, with the defaults Grid::from_params falls back to:

keymeaningdefault
anchor"first" or "fixed""first"
anchor_pricethe fixed anchor, when anchor = "fixed"0.0
stepprice gap between rungs1.0
rungsrung count per side3
sizeorder size per rung1.0
bandhard-stop half-width around the anchor10.0
bounded01a 0..1-bounded market: clamp the band, skip walled rungsfalse
tickthe 0..1 market tick, consulted only when bounded010.001
symbolan explicit symbol overridethe first bar/tick's symbol

The portable Broker surface has no cancel verb

Broker is the common denominator of the backtest and live stacks — submit_market / submit_limit plus the account and market reads — and Grid is designed to need no cancel. The price is that a resting limit placed before a halt or a take-profit cannot be pulled, so the strategy re-flattens every bar while halted and a stray fill from a residual limit is closed on the next event. Inventory stays bounded; the behaviour is documented rather than hidden.

The profile, and the four refusals it must pass

A vike-tradehub profile names the mount and the strategy in the same vocabulary a backtest profile uses, resolved through the same vike_strategy::strategy_by_name registry — so the profile that was backtested is the profile that trades.

venue = "binance"
symbol = "BTCUSDT"
interval = "1m"
seed_cash = 10000.0

[strategy]
name = "grid"

[strategy.params]
step = 40.0
rungs = 4
size = 0.001
band = 600.0

[strategy.params] faces four separate refusals at profile LOAD, and each one is the case the previous passes:

  1. unknown_params — a key no reader reads. A from_params reader ignores what it does not recognise, so sizee = 2 is not an error but size at the compiled default. vike_strategy::PARAM_KEYS declares what each name actually reads.
  2. mistyped_params — a known key carrying an unusable value. and_then(Value::as_…) yields None for a wrong type exactly as it does for an absent key, so quoting a number (size = "2") mounts the default just as surely as misspelling it does.
  3. misrouted_params — a key that is read, well-typed, and then OVERWRITTEN by the mount. resolve_intent_symbol and resolve_intent_venue stamp the MOUNT's own venue and symbol onto every intent, so a params symbol that disagrees with the mount would pass the first two rules and send orders somewhere else. PARAM_ROUTES declares which keys those are.
  4. unarmable_params — a whole table that passes all three and still describes NO ORDER AT ALL. grid is one of the two measured instances: bounded01 = true with step left at its compiled 1.0 is one rung spacing across the entire 0..1 domain, and it was measured at zero broker calls. Grid::arms_no_rung answers that from the same code that rests the rungs. It is decidable only because arm builds its whole order set from the params plus one anchor, once — the wider rule "refuse a mount that would place no orders" is not safe, because a strategy waiting on a market condition places none either.

The mount then ECHOES the RESOLVED configuration, including knobs the profile never states — a log that merely repeats the operator's table is a claim about the operator, and it is false in exactly the case worth logging.

Paper, and what it is faithful to

vike_paper::PaperExecutionClient owns a resting book and fills it with the backtest ENGINE'S OWN public primitives — BarFillModel::fill_price, resolve_intrabar_fills, adverse_fill_price and fee — so "backtest equals paper" is true by construction rather than by review, and gated bit for bit by vike-backtest's tests/r7_gate.rs. The equivalence holds for Gtc / Ioc / Fok; it does not hold for the expiring time-in-forces, where paper enforces the resting deadline and the simulator ignores TIF — a divergence no strategy can reach today, since every request the runtime builds is Gtc.

Getting a live feed behind it

The daemon's DEFAULT paper mount proves the lifecycle and the stdio control channel with NO live feed at all. To watch the grid react to a real market while every order stays on paper, arm the tradehub_live flag, which mounts the same strategy on the real twelve-venue build_node core AND wires that venue's own live feed — and leave the arming ceiling where it is.

policy.venues.<venue> in <project>/settings/policy.toml is a per-venue ceiling (paper < demo < live) that vike_mount::make_engine consults FIRST, above the credential read: a paper venue returns the paper client having loaded no credential, fetched no instrument grid and opened no socket. Its default is paper for every venue, so a box with no [venues] table mounts all paper whatever the credential store holds, and the ceiling can only ever REFUSE — a live line arms nothing by itself. The daemon additionally validates the venue and symbol against the set it has actually wired for live, and hard-errors rather than falling back silently.

Where next

On this page