This part describes, in general terms, how software that acts on markets automatically can be organised. It is a map of ideas, not a blueprint to copy and not a product. Building or running such a system is hard and carries real risk. I mention my own work only once: it is my personal engineering project, described on the Trading platform section of the home page, and nothing below goes beyond a generic picture.
The big picture
- Data to Decision Stored as observed, with its quality checked.
- Decision (to itself) An abstention gate decides if the situation is clear. Doing nothing is a recorded decision.
- Decision to Risk engine Passed as plain data only, so decision code cannot weaken the checks.
- Risk engine (to itself) A fixed rule gives the size, then limits are tested.
- Risk engine to Decision (reply) A rejection is recorded with a reason code.
- Risk engine to Orders Only approved requests are accepted.
- Orders (to itself) The order moves through allowed states, and each change is written down.
Takeaway. Each part has one job, and an order exists only after the risk engine approves it.
Expert viewSeparation of concerns
Each layer has a narrow interface and one reason to change. A key design rule is that the risk engine does not import the decision code: it sees only a proposed order as plain data, so a bug or a clever idea in the decision layer cannot weaken the checks. The same holds for the order layer, which accepts only requests that the risk engine has approved.
Collecting data
Expert viewClocks, calendars and time
Store every record with the time it was observed and, where different, the time it refers to; mixing them up is the root of many subtle bugs. Use one clock source and one time zone convention internally and convert only at the edges. Calendar logic (sessions, holidays, early closes, daylight-saving changes) is a frequent source of errors, so it is worth isolating and testing heavily.
Deciding, and deciding not to
- Data too oldThe last value is not recent enough
- Market closedNo session is active
- Conflicting inputsThe sources disagree
- Limit reachedA risk cap has been hit
- 1Is the last data value too old?Yes Abstain, with a stale-data reason code.No keep going
- 2Is the market calendar showing closed?Yes Abstain, with a market-closed reason code.No keep going
- 3Do the sources disagree?Yes Abstain, with a conflicting-inputs reason code.No keep going
- 4Has a risk cap been hit?Yes Abstain, with a limit-reached reason code.No keep going
- 5OtherwiseThe situation is clear enough, so the proposal goes on to the risk engine.
Takeaway. Doing nothing is a valid decision, and it is written down with a reason.
Expert viewEvery decision has a reason
Logging a reason code for each decision, including each abstention, makes behaviour explainable after the fact and makes tests easy to write: for a given input, the expected code is known. Reason codes should be a closed, documented list rather than free text, so they can be counted and monitored.
The risk engine and the order state machine
- SizeA position size from a fixed rule
- CheckDeterministic limits are tested
- Approve or rejectA rejection is recorded with a reason
- Order moves through statesOnly valid transitions are allowed
Expert viewDeterminism and idempotency
A deterministic risk engine (no randomness, no hidden clock reads, no network calls inside the checks) can be tested exhaustively and replayed exactly. Idempotency means that handling the same message twice has the same effect as handling it once, usually through unique order identifiers, which protects against duplicates caused by retries. An event-driven design treats everything as events appended to a log, so current state can always be rebuilt from history.
Expert viewDesigning the transitions
List every state and every allowed move in a table, then write tests that try each forbidden move and expect a refusal. Treat terminal states (filled, cancelled, rejected) as final. Record each transition with a timestamp, the previous state, the new state and the cause, so that the history of any order can be read like a short story. When outside confirmation and internal records disagree, the usual approach is to mark the order as needing reconciliation rather than guessing.
A human in the loop
Human operator
- Sees state, health and recent decisions
- Can pause, restrict or stop
Automation
- Works continuously and consistently
- Applies the same rules each time
Safeguards: monitoring and a kill switch
- 1Data arrivesCollected under a market calendar
- 2Decide or abstainA proposal, or nothing with a reason
- 3Risk checksDeterministic limits are tested
- 4Order statesOnly valid moves, all recorded
- 5A human watchesHealth, alerts and recent decisions
- 6Kill switchStops new activity at once
Takeaway. The safe default is to stop, and the kill switch is always within reach.
Expert viewFailure modes and safe defaults
Design for the failure first. If a data feed stops, the safe response is to abstain, not to reuse the last value indefinitely. If the state of an order is unknown, treat it as unknown and reconcile with the source of truth before acting. Prefer failing closed (stop) over failing open (continue), and test the failure paths as carefully as the normal ones.
Paper first, then tests and replay
- Paper tradingSimulated orders only, nothing real is sent
- ReplayRecorded data runs through the system again
- Automated testsChecks run on every change
- ReviewBehaviour is understood before anything else
Expert viewReplay testing
Because the risk engine and order logic are deterministic, replaying a recorded stream should produce identical decisions and states; any difference is a bug or a hidden dependency such as the wall clock. Replay also supports regression tests: keep tricky recorded days as permanent test cases. Replay shows what the code does with past inputs; it says nothing certain about the future.
The unglamorous parts
Most of the work in a system like this is not the clever part. It is the dull part: keeping records, protecting secrets, making backups and writing tests. These are what let a person trust the system enough to leave it running, and what let them work out what went wrong when something does. A system that cannot explain its own past behaviour is very hard to improve safely.
- Audit logsA permanent record of what happened and why
- Secrets outside codeKeys and passwords live in a protected store
- BackupsData can be restored if something is lost
- Version controlEvery change is traceable
Automation removes risk
Automation repeats rules quickly, including mistaken ones
A system that passed tests is safe
Tests cover what was imagined, not everything that can happen
A good automated system is one that is able to decide to do nothing, and can explain why.
Frequently asked questions
Does automation make things safer?
It makes behaviour consistent, which helps testing, but it also repeats errors quickly. Safeguards matter.
Why keep the risk engine separate?
So that changes or bugs in the decision layer cannot weaken the checks.
Why paper trading first?
To understand behaviour before anything real is involved.
Is this a guide to build and run one?
No. It is a general explanation only.