In which real money arrives, and three separate bugs prove that "the code looks correct" is not a safety property.
On the 2nd of July the system was armed with real capital. Not all of it - week-two caps of two lots per position and three concurrent positions, deliberately small enough that a total loss would be tuition rather than tragedy.
Within six weeks it had taught me three lessons, each costing real money, and each the same lesson in a different costume.
The routine that closes a spread did the obvious thing: iterate the legs, close each one. Read it and you would approve it.
But it closed the long leg first. For the seconds between that fill and the short's, the account held a naked short option - and the broker, quite correctly, looked at the margin required for a naked short, found it larger than the margin for a hedged spread, and rejected the second order. The system was left holding exactly the position it had been trying to close, having removed its own protection.
The fix is one word: close shorts first. The same asymmetry as Part III, in reverse. Cover before you uncover.
This one is my favourite, in the way a scar is a favourite.
To convert a market order into a marketable limit, the code crossed the spread by 5%:
round(ltp * 1.05, 1)
Perfectly reasonable. Now apply it to a protective wing trading at ₹0.30 - the cheap far-out options this book buys on every single entry:
round(0.315, 1) = 0.3A buy limit of ₹0.30 against an ask of ₹0.35. It could never fill. Not "filled slowly" - could not, arithmetically, ever fill. And it failed silently on exactly the legs that constitute the book's downside protection, on real money, on the 11th of August.
The fix snaps to the tick grid and enforces a minimum cross of at least one tick. Two lines. The bug was invisible in every backtest because backtests do not have tick sizes.
The daily entry is meant to be idempotent - one complex per day, no more. It was guarded by a flag. But an aborted entry - one that failed partway and unwound correctly - left the flag unset, because from its own point of view nothing had happened.
Then the runner restarted mid-session, consulted the flag, saw the day's slot still open, and entered. A real, unintended, eight-lot live position.
Nothing here is a coding error. Every individual piece behaves as designed. The bug lives in the seam between "abort cleanly" and "restart safely" - a state neither component owned.
Three incidents, one shape: not bad code - correct-looking code meeting a real-world detail the specification never mentioned. Tick sizes. Unwind ordering. Idempotency across restarts. No amount of reading the diff catches these, because the diff is fine.
What actually contained the damage was structural, and decided months earlier:
Exhibit A left a position open that should have been closed, the loss was bounded by geometry, not by the correctness of the closing routine.
And one guardrail turned out to be pure theatre: an internal margin cap that computed the book's defined risk and compared it against a percentage of the pool. It sounds prudent. It is useless, because the broker charges margin on a completely different basis - exposure on the notional, not on the defined loss - and the two differ by a factor of three or more. The check passed happily while the real account was nearly out of room. It has since been replaced with a pre-trade query against actual broker margin.
You cannot review your way to safety with AI-generated code, because the failures are not ugly. They are elegant, idiomatic, well-named, and wrong about one detail of the physical world.
What works is structural containment: bounded worst cases, limits that require a commit to change, and a staged path from simulation to small live to full size. Assume the review will miss something, because it will, and build so that missing it is survivable.
This series was written with Claude, the same tool it describes. I supplied the project, the judgement calls and the arguments; Claude supplied the drafting, and dug every figure out of the repository’s own commit history so I could not flatter myself from memory.
Declaring that seems the least I can do given the subject. It would be a peculiar hypocrisy to publish eight posts on harnessing AI while implying I typed them all by hand. If the writing is good, that is partly the tool. If the judgement is sound, that part is mine. Distinguishing between those two things is, as it happens, what the entire series is about.
By day I run product for data strategy and operations at Condé Nast, where the brief is customer identity: the unglamorous business of establishing that the person reading on a phone in Mumbai and the one subscribing on a laptop in London are the same human being. Essentially the ‘slow work’ of turning unknown into known, in various stages. The glamorous parts of my day: developing the single customer view, and harnessing that data to optimise for amplified engagement and revenue across multiple lines and brands.
Twenty-four years of it now, across product, data and technology - client side and agency side, in media and publishing, CPG, insurance, automotive, FMCG and telecom, across North America, Europe and Asia. Enough time in front of CXOs to have learned that a business case travels further than an architecture diagram, and enough time behind them to know the diagram still has to be right.
This project was my evenings. It brings together the triumvirate - my love for the world of finance and markets, my drive to build a production grade system using the latest AI toolset, and the itch to discover first hand what these tools are truly capable of. And the only honest way to find out what these tools can carry is to hand them something that can lose real money.
Questions, disagreements, war stories from your own build, or a conversation about senior product leadership and AI-delivery roles - all welcome.