~/workgambitcase study 03 / 03

Gambit

Fund factsheet · Hamdan Range · 03

Objective

Spec-first autonomous trading system. Research note, then a strategy document with ten named invariants, then code. Circuit breakers, mainnet guards, crash-window order reconciliation. Paper-mode only, and the promotion gate is written into the spec.

Key information

StatusPaper-mode
RoleSole author
Tests passing733
Named invariants10

Top holdings

Rough share of the work; read it as a sketch.

Python70%
pytest suites15%
Exchange APIs10%
Specs / decision log5%
733
tests
~29.6k
loc
3
circuit breakers
10
invariants
30
adrs, binbot

Problem

Software that can lose money while you sleep

An autonomous trading system is a program with a wallet and no supervision. The interesting engineering problem sits under the strategy: every ordinary software failure (a crash mid-order, a duplicate request, a network timeout at the wrong moment) turns into a financial one.

Gambit is my attempt to build that carefully rather than quickly. It runs in paper-mode only. The gate that would promote it to real money is written into the spec and it has not been met, and I would rather say that here than have it come up in an interview.

Constraints

Rules I set before writing code

  • No code before the spec. A research note, then a strategy document, then implementation.
  • Every rule the system must never break gets a name, so it can be referred to in a test and in a code review.
  • Assume the process dies at the worst possible moment, which is between sending an order and recording that it was sent.
  • Real money is off by default and stays off until a written condition is met.

Decision 01

Ten invariants with names

The strategy document lists ten invariants: statements that must hold no matter what the market does or what fails. Naming them changes how the code reads. A test is not calledtest_order_flow_3, it is named after the rule it defends, and when one fails you know what has been violated rather than which line threw.

Alongside it there is a decision log. Every consequential choice gets an entry with the reasoning at the time. It is dull to maintain and it is the first thing I read when I come back to the project after a month away.

Decision 02

Three circuit breakers and a hard stop

The system stops itself on a daily loss limit, on a losing streak, and on drawdown from peak. Each is a separate breaker with its own threshold, because they catch different failures: a bad day, a broken signal, and a slow bleed that no single day would flag.

Underneath those, mainnet guards. Live trading requires explicit configuration that defaults to off, and the code refuses to run against real funds unless that is set on purpose. It is the least clever code in the project and probably the most important.

Decision 03

What happens if it dies mid-order

The failure I designed around: the process sends an order, then crashes before it records having done so. On restart, in-memory state says there is no position and the exchange says there is one.

So every restart begins with a reconciliation. The system reads its own state, reads the exchange, and resolves the difference before it is allowed to do anything else. That is one of the ten invariants, and it has a test that kills the process inside the window on purpose.

Predecessor

binbot, retired

Gambit is the second attempt. The first, binbot, ran to 30 architecture decision records and 446 tests, handled exchange-side OCO orders with a software fallback for venue error −4120, and published its negative results rather than quietly deleting them. I killed it and started again because the design had a shape I could not test properly.

Results

Where it stands

  • 733 passing tests across roughly 29,600 lines of Python.
  • Ten named invariants, each with tests that try to violate it.
  • Three circuit breakers, mainnet guards, crash-window order reconciliation.
  • Paper-mode only. The promotion gate is defined and has not been met. No live trading results exist, so there are none on this page.
  • The repository is private. The spec, the invariant list and the test summary are available on request.

Honestly

What I would do differently

I wrote the risk layer after the execution layer. It works, and the breakers sit where they need to, but a few of them read like something bolted to the outside of the system rather than part of its structure. Risk limits are not a feature, they are a property, and building them second makes them look optional in the code.

The other thing is scope. 733 tests on a system that has never traded real money is a reasonable use of time only if the promotion gate eventually gets met. Left as it is, it is a very well tested demonstration, and I would rather it were a small system that had passed its own gate.

If this is the kind of work your team needs, the contact section is one click away. I answer every real email.