Inside Termondt Tech
I guided an AI to build and run a live trading desk. Here is what it took to make that safe.
At 4:30 Eastern on September 15, 2026, I changed one line in one file on a small cloud server. The line had said sandbox, the broker's test environment. Now it said live. Three minutes later the process restarted, logged in to a real brokerage account, compared what it believed it held against what the broker said it held, found nothing to argue about, and wrote one line to its log: started in live mode.
I'm an hour behind the market, so that was 3:30 at my desk in Illinois, with the close already behind us and nothing the system could do until the next morning. That was deliberate. The first thing I did to the live system was press its kill switch. Twelve minutes after the restart, from my phone, I halted it, watched the halt show up in the audit log, and cleared it with a second authenticated call. Then I left it alone.
I hadn't written a line of the code that did any of this. An AI had, over the previous nine weeks, working from designs, rules and prompts that I directed, reviewed and revised at every step. Sixteen days after the cutover I pressed the same kill switch for real on that strategy, and it has stayed pressed since.
What Argus is, and what it is not
Argus is a platform for fully automated trading strategies. Its first strategy, the one that proved the platform's rails against real money, sold same-day options on the S&P 500 index in pairs called credit spreads. If you don't trade: SPX is the index. Options on it that expire the day they're traded are called 0DTE, zero days to expiration. A credit spread sells one option and buys another further from the current price, collecting a small premium up front in exchange for a larger, capped possible loss. The strategy is popular, crowded, and unforgiving of operational mistakes, because a position opened at ten in the morning is settled by four the same afternoon.
The platform ran the whole loop with no human step in it and a human watching it: it read market data, picked a spread by rule, checked it against risk limits, sent it to the broker as a single order, watched it every few seconds, closed it on a rule, wrote every step to an audit log before taking the next one, and reported to me through a status page and Slack from one small server that was always on through market hours. By October it was running its second strategy, in a different market, as a separate process selected by configuration.
What the first strategy is not: a recommendation, a product, or a strategy that made money. That last point is the end of the story and I'll get to it. The reason to read on isn't the strategy. It's how the platform got built, and what it took to let software that could lose real money be written by something that wasn't me.

Why guide an AI instead of writing it
I've been building software since the late nineties, and trading infrastructure for most of that time. Eight years at the New York Stock Exchange on the order-entry and market-maker gateways, ending as Director of Trading System Development. Three at Envestnet as a principal architect. Six at Chicago Trading Company, where I managed trading-technology squads including an FPGA team, founded the firm's DevOps team, and ran market connectivity for a stretch. For the back half of that career my job was managing engineers more than writing code: design reviews, code reviews, release sign-offs, retrospectives, promotion rubrics.
I could have written Argus myself. It would have taken me the better part of a year, and I'd have learned nothing about the question I actually cared about in 2026, which was whether an AI coding agent, managed the way I'd manage a senior engineer, can be trusted to build a system in which a bug is a withdrawal from a bank account.
So I set one rule for myself on the first day: I would not write the code. I'd direct the design, the engineering standards and the module structure, write or revise every prompt the agent worked from, review every design and every build, and operate the result. The agent was Claude Code.
Between July 14 and October 21 that produced the following.
| Measure | Count |
|---|---|
| Commits | 435 |
| Tagged releases | 51 |
| Lines of application code | about 56,000 |
| Lines of test code | about 59,000 |
| Tests collected by the test runner | 3,540 |
| Test functions, in test files | 2,775 in 125 |
| Decision records | 87 |
| Design documents and release notes | 18 and 39 |
There's more documentation than code, and that's not an accident. The documentation is the control system.
Managing an agent turned out to look a lot like managing a good engineer, with one difference that changes everything: the engineer forgets the whole project overnight. Every session starts new. Whatever we agreed yesterday is gone unless a file says it. So the process I settled into was the one I'd have run with a team, pushed onto paper harder than I ever had to with people.
- A question surfaces: an incident, a review finding, or a doubt of mine.
- I have the agent draft a prompt for the work: the problem, the priority, which existing records to read first, and the standing rules that apply no matter what the task says. For anything touching a safety rail, the first line is fixed: design first, do not implement without sign-off.
- I review and revise that prompt. Most of the actual direction happens here. A prompt the agent drafted and I edited is better than either of us writes alone.
- The agent produces a design that names the alternatives it rejected and why. We review it together. I approve it, amend it, or send it back.
- It builds, with tests. I review the build against the design. It refines and refactors on that review.
- Tests run. A release note records what changed and what the operator should expect to see. I deploy it and read the result back off the running system.
- The decision gets written into a record that every future session is told to read before touching that component.

It was rougher at the start. I was directing module boundaries and engineering practice directly, and finding out the hard way which instructions the agent would hold onto and which it would drop between sessions. The constitution came out of that.
The constitution
Every session began by reading a file called CLAUDE.md. By October it ran to 741 lines, and it's the most important artifact in the project. It isn't documentation in the usual sense. It's the set of rules the agent has to obey no matter what it's asked to do, each with the reason it exists, because a rule without its reason gets relitigated by the next session that finds it inconvenient.
A few of the non-negotiables, quoted as written:
Kill switch reachable in one action, from a phone. Halts new entries immediately regardless of what any other task is doing, including an entry order already working at the exchange.
An automated system's worst failure mode is one that keeps going after you've decided it should stop.
Broker is the only source of truth, always. On every restart, reconcile broker state before resuming automated logic; refuse to resume on any mismatch and alert instead of guessing.
The system's own ledger is a belief. The broker's book is a fact. When they disagree, the belief is wrong until a human says otherwise.
Nothing submits an order outside
pipeline.trades_for_real.
If there's exactly one place in the code that can send an order, there's exactly one place to guard, and a test can prove it's the only one.
A failed audit write halts trading.
The audit log isn't a convenience. It's what reconciliation reads back as the system's expected state. A system that keeps trading without recording what it did no longer knows what it holds.
Never hardcode the COA auction timing.
That one's specific to options exchanges. Complex orders go through a short auction with a configurable duration, and the exchange has changed that duration more than once. A literal in the code is a bug waiting for a rule filing.
The file also has a section called standing landmines: settings that are one changed value away from a daily halt or a silent breach, with the mechanism spelled out so nobody flips them to make a dashboard look tidier. Every entry in that section is there because it was almost flipped at least once.
The day the rule stood on discipline
The audit log is a hash chain. Each record links to the one before it, and the sequence numbers are assigned by the writer from memory. Two writers on one database don't produce an error. They produce two records with the same number, and the chain is broken from there. So the rule from the start was simple: development runs never touch the live database. They use a separate configuration that writes to a scratch directory.
One week in, the agent broke it, and I was the one who noticed. During a diagnostic, capturing baseline log output to compare against a container build, a session ran the bare start command from the repository root. The default configuration path pointed at the live database. The command opened it beside the shadow session I had running, took three sequence numbers, and wedged that session's writer for about eighty minutes. Roughly two thousand records of the day's signal data were never written. Nothing was corrupted, and nothing was lost that mattered to money, because the system was still only watching.
I was frustrated. The rule was clear, and it had lasted seven days. I threw out the day's data, restarted collection, and then did what I should have done on day one, which was stop relying on anyone remembering.
Two enforcement layers went in that week: an exclusive file lock on the database, so a second writer is refused rather than tolerated, and a development profile that cannot point anywhere but the scratch directory. With the lock in place a sequence collision means an invariant is genuinely broken, so a failed audit write halts trading on every path, including writes outside a trading session, with no automatic retry. Six weeks later the last piece landed. There is no default configuration path at all. The bare command raises an error. Every tool requires an explicit configuration argument. The constitution carries the incident under a heading in capitals, and the agent's own persistent notes carry it too, with the instruction never to re-add a default to either.
That lesson shaped everything after. A rule that depends on anyone remembering it will be broken on an ordinary Tuesday, by a person in a hurry or by an agent in a new session with no memory of the last one. If the consequence is money, make the rule a mechanism.
A tour of the rails
Argus has more safety rails than it has strategy. Here are five, each with the failure it exists to prevent and how it's held in place.
The kill switch stops new risk and deliberately does not stop exits. When it was first built, the button set a flag that only the entry gate read, so an order already working at the exchange could still fill after I'd pressed it. An investigation in early August found that and fixed it at three points on the entry side. What the switch does not do is cancel a closing order or stop the watchdog that protects open positions, because cancelling the order that's closing a position would leave you holding exactly the risk you pressed the button to shed. The test makes a real HTTP request to a real control server and checks that a working entry order is aborted, with an exit order as the control that has to survive.
A halted process parks. It does not exit. On August 14, in the sandbox, a reconciliation mismatch halted the process cleanly, and the container runtime restarted it. The restart failed the same check before the control surface had bound its port, so the status page and the kill switch were unreachable for the whole event, through 34 restarts and something like 68 Slack messages, and the only way in was a shell. I noticed because I couldn't open the pages. The rule that came out of it: the control surface binds before the check that can refuse, and an unrecoverable halt parks the process, visible and reachable, until a human acts.
The daily loss breaker is latched per trading date, not per process. It halts new entries when the day's loss crosses a limit, and a restart on the same day reads today's trip and today's starting balance back from the audit log before building anything. Without that, a crash after a bad morning hands the afternoon a fresh loss budget. The size rails have the same shape: every limit is applied once when a size is chosen and again when the proposal is checked, and the tighter one wins.
Reconcile before anything resumes, and an unreadable order halts. At every start the system compares its ledger to the broker's positions and working orders. An order the broker reports but the system can't interpret is not filtered out of the comparison. It's a required input to it, and it halts. The decision record for that one has a line I like: a code comment naming the callers a shortcut is safe for expires the moment one is added.
One submission boundary, proven by reading the source. A test parses every file under the application with Python's own syntax module and fails if any forbidden broker call appears anywhere. It parses rather than searches, so a comment explaining why a name is absent doesn't trip it. The same technique pins that spreads always go out as one multi-leg order, never as separate legs, and that no broker-side stop order exists anywhere. Exits are a virtual stop evaluated in-process against live quotes and handed to a loop that walks a limit order's price, never an unbounded market order.
Going live
The path had three stages, and the constitution forbids skipping any of them. Shadow, in which the system watched and recorded what it would have done, began within the first week. Sandbox, in which real orders went to the broker's test environment, sent its first order on August 13. Live at minimum size came on September 15.
The decision to flip was run as an audit, the way I'd have run a go-live review with a team: 29 findings opened against the tree on September 3, and by September 14 all but three were closed or partly closed, none of them blocking. Of the two gates, one was a rehearsal of the flip that couldn't be separated from the act, so the flip was the rehearsal, and the other was that the daily loss breaker had never been exercised, because the broker's test environment reports a day's profit and loss as zero by construction. I accepted that in writing, with a table of which of the breaker's four parts the first live day would exercise and which it would not.
The first live boot proved two things: the live registration authenticated against the production API, and the account reconciled clean, with no position, order or unreadable-order discrepancies. It did not prove the breaker's trip. At minimum size a day's loss could only reach the limit if a single position went close to its full width, and the findings record says so in bold: do not read a quiet first week as the rail being proven. The first trading day after the cutover was a central-bank announcement day, on which the system places no entries, so it held nothing and proved nothing further, and that was written down the evening before, too.
Two weeks before the flip I caught something the design had never stated. Every account-derived number the system reads, from the loss breaker's baseline to the position count, is a whole-account number being treated as an Argus-only number. The account I intended to use held other positions. One foreign position would have made four rails wrong at once and halted the process at the first reconcile. The account had to be dedicated, and the last non-Argus position in it was closed, under its own management, the day before the flip. We wrote a retrospective on how that gap reached go-live planning undetected. The fix in the design was a required configuration key naming the account, which the loader refuses a live profile without.
The evidence
The first strategy traded live at minimum size for about two weeks of sessions. One of those weeks lost money, and every loss in it came from the same rule firing eight times: a stop that closed a position when the cost to buy it back reached a multiple of the premium collected. The response was a release that changed the stop to one triggered by the index reaching the sold strike, moved the position caps, and added a calibrated deferral of the end-of-day close for positions far from the money. It also fixed, in advance, the conditions under which that change would be reversed.
Then I bought the history. Six years of minute-by-minute quotes for every same-day SPX option, from a data vendor, for forty dollars a month. Before the subscription started, the questions, the scoring, the split between a history set and a held-out period, and what each answer would permit were committed to the repository with timestamps. The scorer was frozen in a second commit. The history set was scored in a third. Nine predictions about the held-out period were committed in a fourth. Only then was the held-out period opened, once.
The strategy as configured on the day of the test, replayed on 1,171 history sessions from January 2020 to September 2025 and 191 held-out sessions from October 2025 to July 2026:
| Measure | History set | Held-out period |
|---|---|---|
| Dollars per session, one contract, fills at the middle of the quote | −2.29 | −13.14 |
| After trading costs at the fill quality the live fills showed | about −19 | about −24 |
| Years with a positive result | 1 of 6 |
One trade in five was stopped out, and a stop cost about what four winning trades earned. Before fees the strategy made about seven dollars a session. Fees were about nine. The design choices held up against their alternatives: the strike-touch stop beat no stop and beat the old stop, and the end-of-day deferral beat always closing. The strategy was simply not profitable, and nothing among the variants tested paid after costs. All nine predictions written before the held-out period was opened came true. Luck gets you eight or more of nine about two percent of the time.
On October 1 I pressed the strategy's kill switch. On October 5 its service was stopped and disabled. The audit log holds the trip, and if anything ever restarts that process it will boot halted and unable to open a trade. The platform didn't stop. Two days later it had its second strategy running in shadow.
What the pre-registration bought was the ability to stop. Without it, every losing week is a reason to tune and every variant is a reason to hope, and a strategy that's below zero after costs can be kept alive indefinitely by an operator who believes in it. I believed in it. My optimism wasn't anchored to any number. It was anchored to the machine. Eleven weeks of watching a system boot, reconcile, trade and halt the way it was designed to, and get fixed fast when it didn't, will make an engineer optimistic about whatever that system is doing, and none of it says a word about whether selling same-day spreads pays. Optimism like that is a position with no stop on it. The pre-registration was the stop, and a stop only works if it's set before the market moves against you. I set it on the morning of September 30, before a single historical quote was pulled. When the answer came back that afternoon, there was nothing left for optimism to do but read it.
The research platform that produced that replay, with its own fail-closed rules about data and pre-registration, is its own story, and the next one.
Three times the loop caught the agent, and once it caught me
The agent was wrong in confident, specific ways, and the defects were real. Counting the July command, I'd call four of them serious. These three are the ones I'd tell another engineer about.
The kill switch that forgot it was pressed. For five weeks, pressing the switch wrote a durable record to the audit log and set a flag in memory, and nothing ever read the record back. Any restart, from an out-of-memory kill, a deploy, or a server reboot, built a fresh switch in the off position, reconciled clean, and resumed entries. No alert fired, because the engage call was never made in the new process. The decision record puts the failure in one line: the operator is not watching, because the operator believes the system is stopped. It was found before the flip and handled design-first. The fix made the halt a fact about the world rather than a fact about a process. On every start the switch is rebuilt from the audit log, engaged if the latest trip has no later resume, with no expiry, no date bound, and no automatic clear. The only thing that can clear it is an authenticated human call that records before it flips, so a failed clear is a no-op. The evening it shipped to the sandbox, the first boot of the new image came up engaged, restoring a halt from four days earlier that four sessions in between had traded straight through. Thirty-three tests cover it, and four deliberate mutations were verified to fail them.
A function that treated silence as permission. When a closing order has been abandoned and the system wants to send a more aggressive one, it first asks the broker whether the first order is dead, because two closing orders filling on the same spread doesn't flatten the position on expiry day. It re-opens it, inverted and unmanaged. The check asked whether any returned order was still working. In Python, any() over an empty list is false. So an answer that didn't mention the order at all passed the same test as an order the broker had positively confirmed cancelled, and authorized the second order. Two modules away, the order-repricing loop met the same silence every cycle and correctly treated it as a read gap rather than a verdict. The stricter of the two call sites had the weaker rule. This one was found by reading, in a review pass over the exit path that I'd asked for, not by an incident. The rule that came out of it is in the constitution now: never derive settled from the absence of a working order.
Spreads that hid from the count. On August 17, in the sandbox, the system opened two spreads minutes apart that shared a strike: one spread's sold leg was the other's bought leg. Positions were stored by symbol and netted, so the shared strike netted to zero and its row was deleted. Reassembly then couldn't attribute either spread, and neither was being watched. Worse, the position count the concurrency limit read was derived from the broker's legs, and the broker nets the same way, so two spreads presented as one and a third was allowed through a cap of two. Chain three spreads and every interior strike disappears: the count reads one forever, and the limit stops bounding anything. The fix attributes every holding to the trade idea that created it, from per-order fill records that already existed and that nothing had been reading, and the gate takes the higher of the broker-derived and ledger-derived counts. The constitution now says, in capitals, never to reintroduce a symbol-keyed count on either path.
And once the gap was mine. The dedicated-account requirement in late August was missing from my own specification, and no amount of agent review was going to find it, because the agent had been told the account was fine.
What it cost
| Item | Amount |
|---|---|
| Calendar | 14 weeks, July 14 to October 21 |
| My time | about 30 hours a week, roughly 420 hours in all |
| Market data history, one subscription | $40 a month |
| The server, 2 vCPU and 4 GB | $29 a month |
| AI tooling | $20 a month at first, plus usage credits when the limits hit; then the $100-a-month plan, after which no credits were needed |
| Capital at risk | the minimum position size the broker allows, in a dedicated account |
The money lost trading was small and isn't the point of this piece, so it isn't in it. The time was the real cost, and most of it went to prompts, reviews and operating, not to waiting for code.
What I would tell a CTO
If your team is about to let an AI agent write software that can hurt you, these are the rules I'd start with. Each one cost me something to learn.
- Treat the prompt as the first artifact under review. Have the agent draft it, then edit it yourself. A prompt nobody reviewed produces a design nobody specified.
- Write the constitution before the code, and load it into every session. The agent has no memory between sessions unless you give it one. The rules file is that memory, and it has to be read first, every time.
- Every "never" carries its reason. A bare prohibition will be relitigated by the next session that finds it inconvenient. One with its incident attached won't be.
- Give each dangerous action one boundary, and pin it with a test that reads the source. One function that can send an order, one that can clear a halt, one that can write a position. A test that runs the code proves the code behaves. A test that parses the code proves the forbidden thing doesn't exist anywhere else. You need both, and the second kind is the one agents never write on their own.
- Decide what fail-closed means per rail, and write it down. A halt that stops the process is right for a corrupted ledger and wrong for a panic button. Whether to halt and how wide the halt reaches are different questions. Merge them and you get a system that cancels the order protecting you.
- Never let the stage that proposes be the stage that ships. Design, sign-off, build, review, test. The agent will happily implement a change to the kill switch in the same breath as it proposes it. Don't let it. I wouldn't have let an engineer do it either.
- Make the mistakes mechanical, yours and the agent's. No default that points at production. No command that does the dangerous thing when run bare.
- Pre-register what would make you stop, before the data arrives. Write the test and the threshold while you still believe.
- Budget for review at every stage, and treat sandbox incidents as cheap tuition. Every serious defect above was caught by me watching, by an incident in the test environment, or by a review pass I asked for. None of them surfaced because the agent noticed on its own while building.
What is next
The platform now runs its second strategy, shadow-only, in a different market, with its own process, configuration and record on the same rails. Its rules were signed before its first scored day, and it's being scored against a pass rule fixed in advance. Whether it earns real capital is a question the evidence will answer, the same way it answered the first one.
If your team is putting AI-written code into production, I run a two-week Rails Review. I read your agents' prompts, their output and your tests, and hand back the written constitution and the pinned invariants your system is missing, in the form you've just read about. If you run a trading or investment operation that needs execution, risk controls or reconciliation built to run automatically, and to be monitored and stopped by a human, that's what Termondt Tech does, and what I did for fourteen years at NYSE and Chicago Trading Company. Either way, use the contact form.
Nothing in this article is investment advice. The strategy described lost money and was shut down.
Glossary
| Term | Meaning |
|---|---|
| SPX, SPXW | The S&P 500 index, and the ticker root for its weekly and daily options |
| 0DTE | An option expiring the day it is traded: zero days to expiration |
| Credit spread | Selling one option and buying another further from the current price; premium in, capped loss out |
| Short strike | The price level of the option sold; the stop fires when the index reaches it |
| Complex order auction (COA) | A short exchange auction that multi-leg orders pass through before resting; its duration is set by the exchange |
| Reconciliation | Comparing the system's own ledger of positions and orders against the broker's, before anything is allowed to trade |
| Audit log | An append-only record of every decision and action, each entry hashed together with the previous one |
| Kill switch | The one-action control that stops new entries while leaving exits and the watchdog running |
| Watchdog | The always-running task that evaluates the stop and the end-of-day close on every held position |
| Sandbox | The broker's test environment, with real order handling and no real money |
| Pre-registration | Committing the questions, the scoring and the stopping rule before the data is seen |
Rich Termondt spent 25 years building trading infrastructure, including the order-entry and market-maker gateways at the New York Stock Exchange and the market-connectivity, FPGA and DevOps teams at Chicago Trading Company. At Termondt Tech he now guides AI agents to build fully automated systems that a human can monitor and stop; Argus was written by Claude Code under his direction and review, and its first strategy was shut down on his evidence.