Skip to main content
INTERNAL TOOLING

A retried order cannot become a second order

A paper trading lab, simulated money only, with stable order ids, a pre-trade risk gateway and a hash-chained audit log.

THE PROBLEM

What was in the way

Most of the cost in a system where mistakes are expensive sits outside the feature. A request times out after the other side already accepted it. A limit exists only in someone's head. A log cannot prove what actually happened.

This is a personal project built to work on exactly that, against a simulated broker account. Nothing here trades real money, which is the point. A paper account is the cheapest place to make the expensive mistakes.

THE APPROACH

How it was built

Four seams, each a module that could become its own process later. Signal code is pure and may not import the broker. Risk is a separate gateway that validates an order or refuses it. Execution is the only place the broker SDK appears. The audit log is append-only and hash-chained, so an edit to any earlier entry breaks every hash after it.

One rule carries most of the weight. Every order id is derived from the signal that produced it, so a retry after a lost acknowledgement lands on the same broker-side dedup key instead of placing a second order.

Signal to audited order
Signal to audited order
HIGHLIGHTS

How it works

Same bar, same order id

The id comes from the signal, anchored to the bar's own timestamp rather than the wall clock. A re-run of the same bar reaches the same broker-side dedup key instead of filling twice.

Rejections and timeouts are opposites

Transport errors back off with full jitter up to a capped attempt count. A broker rejection is terminal, and a programming error fails fast rather than being retried as flaky network.

Limits fail closed

Order caps, an exposure cap and a daily-loss breaker gate entries only, so an exit is never blocked. An unreadable equity anchor trips the breaker instead of quietly disabling it.

The log has to agree with the broker

One command walks the hash chain and checks every link. Another diffs broker orders, fills and positions against the log, then exits non-zero on a break so a scheduler can react.

RESULTS

What it does now

same signal, same order ida retry cannot become a second order
exits are never blockedcaps and the loss breaker gate entries only
log diffed against the brokerreconcile exits non-zero on a break
STACK

What it is built on

Pythonpipeline and CLI
alpaca-pybroker SDK, paper account
pandasbar data and indicators
Clickcommands with ops exit codes
pytestunit and CLI tests
mypytype gate, enforced in CI

Got something in your workflow that looks like this?