A position looks wrong. An order is stuck. A risk check returns an unexpected result. In trading systems, those are familiar reasons for a late-night call.

I spent more than ten years working in financial institutions, mostly on the sell side. When I first moved closer to the buy side, the teams, trading goals and terminology changed. The operational problems were familiar: important business state was spread across services, databases, caches and queues, and understanding a failure meant piecing together logs, dashboards and manual repairs.

I also kept seeing teams rebuild the same foundations. Matching, risk checks, hedging and order management have different business rules, but each processes inputs, reads current state, makes a decision and records the resulting changes. Ordering, persistence and recovery took up a large share of the work every time.

A hedging engine brought that problem into focus.

A decision with real consequences

The engine processed several kinds of input:

  • hedging instructions and risk configuration;
  • market data and trend signals;
  • order and position updates.

Before deciding whether to place or cancel an order, it needed the current positions, open orders, risk settings and the state of the risk-off switch. A missed request could leave an exposure unhedged. A duplicate could add exposure. A late cancellation could leave an unwanted order in the market.

The hedging engine combines incoming signals with current positions, orders and risk settings to decide whether to place or cancel an order.
The hedging engine combines incoming signals with current positions, orders and risk settings to decide whether to place or cancel an order.

To explain a decision, we needed to know which input arrived, what state the engine read, which rules it applied and what changed. We also needed to connect that decision to the order request it produced.

The advice that helped most was to model the engine as a state machine.

Making decisions repeatable

The model gave the engine a clear job: process explicit inputs in order, keep its own business state, and produce a repeatable result. Given the same input, starting state and business rules, it should make the same decision. That is what deterministic execution means here.

If a trader asked why an order was placed, we could follow the decision through the input, risk settings, position and open orders to the resulting state changes and order request. Replay would let us examine the same decision again.

Running the decision logic one command at a time also removed a large source of concurrency bugs. Work around the engine could still run in parallel: receiving market data, communicating with order gateways, writing to disk, updating indexes and monitoring the system. The business decisions themselves would follow a defined order.

Each input is evaluated against the current state, producing state changes and an explicit order decision.
Each input is evaluated against the current state, producing state changes and an explicit order decision.

This made the logic easier to understand. It also made the next problem more obvious: the engine had to preserve those decisions through failures and restarts.

What the state machine still needed

Once the engine became responsible for critical decisions, its recovery behavior mattered as much as its business rules. A stopped process could interrupt trading. A process restarted with the wrong state could issue the wrong order. If the last committed decision was unclear, someone still had to reconstruct it by hand.

These systems had run in production for years, with reliability mechanisms spread across application code and operational procedures.

Explaining a decision took detective work. Application logs, database rows and queue offsets each held part of the answer. There was often no complete execution history from which to reconstruct the decision.

Failover required more than restarting a process. A replacement needed the correct state, the right position in the input stream, and a record of which decisions had committed and which outputs still needed delivery.

Upgrades depended on manual procedures. Even a small service could require a long checklist, carefully planned downtime and a recovery plan if something went wrong.

Performance work was difficult for similar reasons. When business logic, persistence, event delivery and recovery were tightly coupled, changing one part could affect the behavior of another. It was hard to establish what an optimization was allowed to change.

Even record versions required more care than expected. A handler might validate several fields, update multiple records, then return early on an error. If each handler managed version increments itself, a small code change could increment a version twice or miss an update. Those rules needed to be enforced consistently by the engine.

The state machine gave us a way to organize decisions. Making it dependable in production still required durable history, reliable recovery, clear upgrade rules and predictable execution.

The foundation I wanted to reuse

I wanted business teams to be able to define their models and rules without rebuilding that infrastructure for every system. Inputs and outcomes should be explicit. Changes should be traceable. Recovery should reconstruct committed state, and replay should reproduce the decisions that created it.

That became the motivation for StateVec: a reusable transaction engine that takes responsibility for execution, consistency and recovery, while leaving business decisions in the hands of the people who understand them.