Automated trading

Automated trading software runs a trading rule without a person at the keyboard: it takes a market-data feed, evaluates the rule as data arrives, and sends the resulting orders to a venue over an authenticated connection — then keeps track of what came back, because an order that was sent is not an order that filled.

The hard part is not the automation. It is everything between the rule and the wire: proving that the code you tested is the code that trades, keeping a mistake from arming an account you did not mean to arm, bounding what a single order can do, and stopping cleanly when something goes wrong. Vike Trader's answer is one strategy trait that runs on four runtimes, a headless daemon that mounts it, and four independent gates between an order and the venue — every one of which defaults to closed.

One strategy, four runtimes

There is one strategy trait. It is generic over its broker handle — the strategy's window onto whichever execution stack is running it — and a strategy never learns which one it is talking to. In a backtest the simulator implements that handle with direct mutation; on the live and paper stacks the per-bar context implements it by buffering submissions, drained after your handler returns through the one live path: mint the client order id, cross the risk gate, hand it to the venue client.

That gives one progression, and each step changes exactly one thing:

Backtest
Historical data, the simulator, the full cost and fill model. See strategy backtesting.
Paper
A live feed, the real execution core, and a resting book behind the same execution seam a venue sits behind. The paper exchange is not a stub that acknowledges orders — it is built from the backtest engine's own fill primitives, and a merge gate proves the two produce bit-identical fills. This is the default for every venue.
Demo
The venue's own demo or testnet account, over the real adapter: real signing, real websockets, real rejections, no real money. Reached by putting demo credentials in the credential store and raising that venue's ceiling to demo.
Live
The same adapter against the production account. Reached only when the arming ceiling, the credentials and the venue's own second gate all agree — see below.

Nothing about the strategy changes across those four. A strategy that needs simulator-only machinery is written against the concrete simulator broker instead, and the type system then documents that it is not live-portable — portability is pinned by compilation, not by a comment.

The headless daemon

Unattended trading runs in a daemon with no GUI. It takes a TOML profile, mounts the strategy the profile names, accepts newline-delimited JSON commands on standard input, emits a periodic one-line JSON snapshot summary on standard output, and survives an SSH disconnect because it runs as a service rather than in your login session. It ships as a container image and as a native Linux binary with a systemd unit.

The strategy is a profile field — the same name and parameters table the backtest profile uses, resolved through the shared registry, so the profile that was backtested is the profile that trades. A Rhai script mounts by path instead, and the daemon logs the script's path and its hash at startup, which is the log's answer to "which code traded".

The profile is refused at load, not at the first order

A profile naming a strategy that is unknown, simulator-only, or one that would resolve and then never trade is rejected when the file is read. Four further refusals cover the parameters table, each catching the case the previous one passes: a key no reader reads; a value no reader can take; a key the mount overrides — including the ones naming which instrument and which venue real orders would go to; and a table that passes all three and still describes no order at all. The mount then echoes the configuration it actually resolved, so you can read what it will do rather than infer it.

Several strategies sharing one venue account can share one process: the profile takes an array of mount blocks, each mounting under a derived controller id, and two blocks that would derive the same id are refused at load rather than silently colliding at runtime.

Watching it and driving it

With an address configured and an observe key in the credential store, the daemon starts an authenticated node server so a laptop GUI can watch this node's snapshots; without either, it starts nothing and keeps trading headless. Trading it remotely needs a second flag and a second key. Every control surface — the socket, the terminal REPL, an agent — goes through one acceptance path, so the rate token, the notional cap, the rationale sanitizer, the single-writer lane and the audit record are identical by construction rather than by review. A remote submit must carry a client order id it minted itself, because a peer whose id the runtime minted has no stable handle to cancel or deduplicate by.

The per-venue arming ceiling

The first gate is a table in the project's policy file:

[venues]
binance = "demo"
bybit   = "live"

Each value is a mode — paper, then demo, then live, in that order — and it is a ceiling. It is read first, above the credential read: a venue capped at paper returns the paper client having loaded no credential, fetched no instrument grid and opened no socket.

The ceiling can only ever refuse. Combining a ceiling with a venue's actual capability takes the lower of the two, never the higher, and that is a named operation rather than a one-character call at each site — because the two spellings read equally plausible and differ by "the file can only refuse" versus "the file can arm real money". So a line reading live does not put a venue live; it declines to stop that venue going live if its credentials already say so.

A box with no venue table mounts everything paper, whatever the credential store holds. That is the one settings default in the platform that changes behaviour, and it is deliberate: the absence of a file must be the safest answer, not the most permissive one. The process warns once at startup naming the file, the key and every venue it refused, rather than refusing to start — a venue connection is a capability, and losing one should degrade the session rather than kill it.

The measured defect this closes: a daemon whose profile mounted one venue opened nine authenticated live sessions nobody asked for, purely because credential pairs for other venues were sitting in the same file.

Behind the ceiling sits the older rule: absent credentials are the live gate. A venue with no keys in the store stays paper. Three refinements sit on top of it — a venue's mainnet switch is an explicit flag rather than an inference; a key known to carry withdrawal permission degrades that venue to paper instead of being discovered at the first order; and a venue whose only tier is real money is gated twice, once for execution and once for reconciliation, both off by default. A live mount also refuses to start without an account-dependent risk budget, naming every missing key, because no universal safe default exists for a per-order notional ceiling.

The pre-trade risk gate

Every order crosses one gate before it reaches a venue client. It is pure — it owns no bus and publishes nothing; the engine acts on its verdict — which is what lets the same rules run identically in backtest, paper and live. A veto is a terminal, pre-venue denial event in every mode, so a limit you proved in a backtest is the limit that stops you live.

Its limits come from two places: the venue's own fetched instrument grid, and the run profile's risk table.

LimitWhat it does
tick / lot sizeRounds the order onto the venue's grid, round-half-to-even.
min qty / min notionalFloors, on opening orders only — a pure reduce is exempt, so a position can always be closed.
max notional per orderThe per-order ceiling. It lives in the policy file, and that settings type implements neither the environment nor the command-line override trait — so raising it from a stray environment variable is a compile error rather than a review comment.
max total exposureA cap on projected open notional. Read the note below before sizing it.
order rateA submission-rate throttle over a rolling window.
max leverageThe declared cap; enforcement is the initial-margin requirement derived from it, so the two cannot disagree.
price collarThe fat-finger axis: an opening order whose limit or trigger price sits further from the mark than the larger of a percentage and an absolute floor is denied. Both halves are required — a percentage is useless on a cheap instrument, a floor on an expensive one.
slippage budgetAn opt-in walk-the-book veto: reject an order whose projected fill against the displayed book exceeds a basis-point budget, or that the book cannot fill at all.

Notional includes the instrument's contract multiplier, computed exactly as the backtest fill gate computes it, so live and backtest verdicts agree. A multi-leg combo crosses the gate as one atomic batch — every leg passes or the whole combo yields a single denial naming the failing leg — and consumes one throttle slot, because it is one venue order. A modify is judged against the projected order, the resting request with its new terms folded in, never against the delta; its veto is non-terminal and consumes no slot.

The total-exposure cap is scoped to one symbol at one venue, despite its name. It is not account-wide, not cross-symbol and not cross-venue: one execution engine is built per venue, so there is one gate and one copy of this cap per venue. Trading several symbols means you are protected by that number once each, never once in aggregate — a value sized for a whole book is a much weaker cap than it looks. The name is kept because it is also the operator-facing key, the string in the live-mount refusal, and a field inside the journal's state hash; the scope is pinned by a test rather than left to be discovered.

Halt and the automatic stops

A halt must stop opening risk without trapping anyone in a position, and that distinction drives the whole design. Under a halt the risk gate — which holds the position book — admits only a position-verified reduce: the order must oppose the position and be covered by it. The execution-client boundary does not hold that book, so it takes the caller's reduce-only flag as the only evidence in existence; a stricter mode additionally refuses a submit the venue's own position report proves would open risk, and there every unknown admits — never fetched, the fetch failed, an answer for another account — because the promise being kept is halting cannot trap you, not halting is airtight.

Two dead-man's switches engage a halt without you: one on a link the venue's own bridge reports dead, one on a feed that has gone silent. Each runs on its own window.

And a panicking handler does not kill the process and does not let it keep trading. Every dispatch runs under a catch: on a panic the core enters safe state — the risk gate denies every new order, a best-effort cancel is issued for every non-terminal working order, and the fault is surfaced in the snapshot. A possibly-inconsistent engine is never traded on again, only read for display, until the process restarts.

The latency budget, measured

One thread owns every piece of mutable trading state and is its only mutator. Everything else in the process reaches that state through a single, totally-ordered queue and reads it back through an immutable published snapshot — so determinism is a property of the architecture rather than of getting the locking right.

Three lanes feed that queue, and each has a different contract with its producer:

  • Execution events — fills, order transitions, account and funding updates, liquidations — are lossless. A venue adapter blocks rather than drops, so a slow core back-pressures the venue task and never the GUI.
  • Commands from a GUI or a control surface never block their caller. A full-queue rejection returns to the caller and is counted in the snapshot, so it cannot be silent.
  • Market data goes through a latest-wins conflation slot with a drop counter, so a slow core sees the freshest price instead of an unbounded backlog of stale ones.

The gate

That drain loop is the one thread in the platform with a latency budget, and the budget is enforced rather than aspired to. A test in CI pushes a hundred thousand venue-reply events through the core on a dedicated runner whose cores are reserved for it, under real-time scheduling, and asserts the whole tail — not a single percentile. The core-hop budget is 10 µs at the ninety-ninth percentile, with separately derived ceilings above it for the far tail, the maximum, and the count of hops over a hundred microseconds.

10 µscore-hop p99 budget, asserted in CI
tailgated, not the median — the tail is what costs a maker money
reserved coresa dedicated runner under real-time scheduling

Two properties of that gate are worth knowing, because they are what makes it a measurement rather than a slogan. First, it asserts the tail: before it did, a reference run went green while carrying one core hop over a hundred milliseconds, because the percentile that looked good was the one being checked. Second, only three of the harness's variants assert a ceiling at all — the rest measure and record without gating, deliberately, because an uncalibrated ceiling is a flaky gate and a flaky gate teaches people to re-run rather than read. Deriving one of the ceilings that does gate honestly took ninety-eight CI attempts.

The consequence for anything running on that thread is a rule: nothing on the fold path may log per message, and everything that can run off the fold does, consuming the published snapshot, event and journal streams from outside the loop.

Driving it from an agent

The command-line tool is also an MCP server over standard input and output, so an AI client can create strategies, run backtests and place orders through the same verbs a person uses. It is a wire client to a running node rather than a second trading stack: there is no separate engine for an agent to be wrong in.

Order writes pass local notional and quantity guardrails before any venue is reached, and then cross the same acceptance path and the same risk gate as every other surface. Reads that would disclose a credential do not exist: the secrets verb prints key names and the store's location, never values, and the one write it has replaces named keys in place rather than regenerating the file.

Run it yourself.

Every venue mounts paper by default. Nothing below trades anything until you say so.

Free. Source-available under FSL-1.1; every release becomes Apache-2.0 two years on.

Trading involves substantial risk of loss and is not suitable for every investor. Nothing on this page is investment, financial or trading advice, and no result shown or described — backtested, simulated, paper or live — is a promise of future performance. Backtested and simulated results in particular have inherent limitations: they are produced with hindsight, do not reflect every real-market condition, and no representation is made that any account will achieve results similar to them. Vike Trader is software; orders sent from your accounts, the credentials that authorize them and the decisions behind them remain yours. Consider your circumstances, and never trade money you cannot afford to lose.