NineFifteenAM

Mistakes that cost me

Seven bugs in my trading bot that never threw an error

4 min readBy NineFifteenAM

A strategy that could never trade, an order that was silently never placed, a data feed that recorded zero rows for a session. The quiet failures from six months of running a trading bot, and the checks that catch them.

Short answer

The most dangerous bugs in a trading bot don't crash. A number just stops arriving, or an action reports success without happening. The defence is not more try/except blocks but coverage checks: assert that every strategy, feed and order path produced something in the last session, and fail loudly when one is empty.

When my bot crashes, I find out in seconds. The bugs that cost me the most time, and some money, never crashed at all. Nothing errored. A number just stopped arriving, or a function said it had done something it hadn't.

"Quiet failure is the house failure mode" is now the first line in the notes file my coding assistant reads before touching the bot. Here are the seven that earned it that place.

1. A strategy that could never take a trade

I ported an EMA trend strategy from an open-source collection. It ran every day for weeks, alongside a hundred others, and took zero trades. Not a bad strategy — an impossible one.

The warmup guard said "don't trade until you have 52 bars of history". The history buffer was capped at 50 bars. The condition could never become true.

Reviewing it turned up more: the port kept 3 of the original's 14 entry conditions and used different moving-average periods on a different timeframe. It was not the strategy I thought I was testing.

What catches it: a daily check that every enabled strategy has either taken a trade or at least evaluated a signal recently. A strategy with zero activity for a week is a bug until proven otherwise.

2. A signal that fired and an order that was never placed

On 18 August one strategy produced a correct SENSEX signal. No order reached the broker.

The strategy called engine.enter(). The engine's method is called buy(). Python raised an AttributeError, a broad try/except caught it, and the handler logged it at debug level — which nobody reads during market hours.

What catches it: never swallow an exception on an order path. Log order failures at warning or above, and after each session compare the bot's list of intended orders with the broker's order book.

3. A data feed that recorded zero rows for a whole session

I record option-chain data around the close for both NIFTY and SENSEX. On 19 August SENSEX recorded 379 rows. NIFTY recorded zero. No exception, no log line.

The code built option symbols by hand. Zerodha names weekly expiries like NIFTY2682524050CE and monthly ones like NIFTY26AUG24050CE. That week the nearest expiry was a monthly, so every symbol pointed at an instrument that didn't exist, every quote came back empty, and the recorder wrote nothing. It would have repeated every month, silently, forever.

What catches it: never construct broker symbols; look them up from the cached instrument dump. And a coverage check: every expected source must write a non-zero row count for the last session.

4. A setting that said "done" and changed nothing

The dashboard lets me change a strategy's quantity. For one whole family of strategies, the call returned success, the dashboard showed the new number, and every new trade kept using the old quantity.

Those strategies read their size once at startup into a private field. The setter updated a different attribute. A second bug in the same path showed the quantity as zero in the dashboard because the config key used a different suffix.

What catches it: a test that checks the effect, not the return value — change the setting, then assert the next order uses it.

5. A zero that moved the day's low by 24,538 points

Before the first tick arrives, the bot's current price defaults to 0.0. One code path updated the running day-low from that value. The day's low became 0, the day's range became about 24,538 points against a normal budget of around 83, and every strategy's direction score flipped.

This one happened after the close, so no trades were affected that day. That distinction matters: "the bug exists" and "the bug bit today" decide whether you fix it tonight or this week.

What catches it: guard running minimums and maximums against zero and None before updating them.

6. Replayed trades written into the live trade log

My replay engine re-runs past sessions through the live strategy code. Some strategies write their own trades to the database. During replay, they wrote replayed trades into the live trade log, mixing history that never happened with history that did.

What catches it: the replay worker now replaces the database write functions with no-ops before any strategy is imported, and reports how many writes it blocked.

7. A field that was wrong for 47.5% of trades

Every trade records the nearest key price level and whether the trade was heading toward it. The original calculation measured distance to the nearest level in either direction. For 1,790 of 3,768 trades, the "nearest level" was behind the entry, not ahead of it.

Nothing traded on this field directly, but I had been reading analysis built on it. I replaced it with a direction-aware distance and backfilled the history.

What catches it: spot-check derived fields by hand against a chart before trusting any analysis built on them.

The pattern

Every one of these passed every test I had, because my tests asked "did it run?" and not "did it produce what it should?". The fix is the same each time:

I would rather the bot crash loudly at 9:16 than run smoothly all day doing nothing.

Questions people ask me

What is a silent failure in algo trading?

A fault that produces no exception and no error log — a strategy that never enters, an order call that is caught and logged at debug level, a feed that returns empty results. The system looks healthy while part of it does nothing.

How do I detect silent failures in my trading bot?

Check outcomes, not return values. After each session, assert that every enabled strategy evaluated signals, every expected data source wrote a non-zero number of rows, and every order the bot believes it placed exists in the broker's order book.

Should I construct option symbols myself for the Kite API?

No. Zerodha names weekly and monthly expiries differently, so a hand-built symbol breaks the week the nearest expiry becomes a monthly. Look symbols up from the instrument dump instead.

bugsmonitoringtestingbroker apidata quality
N

NineFifteenAM

One trader building an options bot for Indian index markets since early 2026. I write down how it is built, what broke, and what it cost — no tips, no calls, no returns.

Related

24 Sept 2026
I switched a strategy to paper. Thirty-three seconds later it traded real money.A live-routing bug in my trading bot let a rule outrank my manual paper switch. It cost ₹15,947, and fixing it exposed a second bug that could have sold a paper position on the real broker.
Mistakes that cost me
26 Sept 2026
Algo trading in India: the rules, the cost, and what to do firstWhat the law requires of a retail algo trader in India, what a system costs to run after the April 2026 STT change, and the order to build it in.
Guides
27 Sept 2026
Nine rules I use so my trading data can't fool mePoints instead of rupees, resampling days instead of trades, an out-of-sample findings register, and the other analysis rules I wrote after my own trade data misled me — each with the example that caused it.
Best practices