Quant Trading Bot Devlog

한국어로 보기

[Jul 13-19] Before the First Real Order, I Pulled Out Everything I Couldn't Trust

A 15-second WiFi outage set a robustness principle in stone, and auditing the code that actually moves money concluded "don't place the first order yet"

This week sat right before actually expanding live trading. So the question in front of me was less "what should I build next" and more "how much can I actually trust what's already here."

From Monday to Saturday, something I'd been taking for granted — data, AI, network, the order-placing code — got overturned once each. Those doubts ended up setting the week's agenda.

It started with data — and turned into "don't fix it, prevent it"

The early-week findings were all the same shape: things that looked fine on the surface but were quietly wrong.

An AI reviewing a report flagged one company's earnings as "unprecedented" and confidently called it data corruption — turned out to be real. What the model treated as "unprecedented" was just the edge of its training-data knowledge, not the actual market history.

The same day, I caught a bug where correct financial figures were being labeled under the wrong company's name.

Tuesday brought something more alarming: two of the most frequently mentioned large-cap tickers had been completely missing from the data for weeks, because of a flaw in the name-recognition logic.

Less important tickers were accumulating fine — it was specifically the two most important ones that were empty. Fortunately the raw source data was already archived, so fixing the recognition bug alone recovered tens of thousands of records instantly.

Wednesday turned up the same class of bug again — this time a common abbreviation for a large ticker missing from the recognition list.

That's when I changed approach. Seeing the same pattern repeat for what felt like the umpteenth time, I decided to prevent recurrence instead of just patching it again.

The ticker-name recognition logic had been scattered across the project, each copy implemented separately, so fixing one spot left the others stale. I pulled it into a single shared module that everything now imports. There's exactly one place left to fix.

This news/board collection work lives under the data archive(new tab), and this week I also started collecting stock-discussion-board data there to capture retail sentiment.

To avoid contaminating an ongoing comparison experiment, I collected the data but held off on actually using it. Data decays, so collect it now; the decision isn't urgent, so make it later.

The turning point: a 15-second WiFi outage

Thursday was this week's turning point.

Home WiFi dropped for about 15 seconds overnight, but digging through the logs, the actual damage lasted 4 hours 45 minutes. It reconnected, but sat in a half-broken state with zero actual data flow for hours.

What stung more: the OS had correctly known within 4 minutes that the internet wasn't working. The signal existed — nobody was reading it.

The same night, a bigger incident overlapped: overnight automated analysis stalled less than halfway through. Three problems hit at once, and the network issue was one of them.

That incident finally settled a principle I'd been putting off: don't tie the thing that should be safest to the success of the thing that's riskiest.

Recording the analysis input is light work that finishes in minutes; the heavy computation after it takes hours and is exposed to things like network failures. But the structure had that light input record only finalize once the heavy computation fully succeeded.

I reversed the order — input gets saved securely first, then the heavy computation starts.

A few more principles followed from this. Stale data and wrong data need different treatment — deciding off data that's a few days old isn't very risky, but data from mixed time points producing internally inconsistent numbers must stop unconditionally.

Alerting follows the same logic: if there's nothing the recipient can actually do right now, it's not urgent — batch it for later.

This WiFi auto-recovery daemon lives separately as the network watchdog program(new tab), and on Friday I added monitoring for remote-access disconnects too.

The network being fully down and just remote access dying carry different risk levels. The former can justify even a reboot; the latter might mean the machine is still running fine, so recovery is limited to gently restarting just that program.

Before expanding live trading, I pulled out everything I couldn't trust

Friday and Saturday were about doubting what already existed.

On Friday, switching to batching data up front for the AI made things faster, but reports suddenly started coming back in English. Embedding Korean data inside English-language system instructions had apparently led the model to infer it was in an English conversation.

The format itself turned out to be a signal to the model, not just the content. Fixed by explicitly requiring narration in Korean regardless of source language.

Then Saturday hit the most important point of the week.

The code that actually places orders is the most carefully handled part of this project. An outside audit before real use concluded: don't place the first real order in the current state.

Several genuine bugs turned up — a duplicate-order safeguard that silently passed when its own check failed, and a path where an order still went out even if recording it had failed.

Individually small, but every one was the kind that "normally doesn't trigger, only under specific conditions" — unfindable without an audit. Since this is the only path touching real account funds, it was worth the time.

I also reconsidered which broker to use. First pass said "no need to switch," reversed within a day.

Right now — before any real order has gone out — is the cheapest point to switch brokers. A few weeks in, switching would mean throwing away accumulated verification and familiarity; right now there's nothing to lose.

Shelved what needed shelving

One long-standing idea also got settled this week.

Finally shelved running a lightweight AI judgment continuously during market hours. Fast-reacting intraday information is mostly already priced in, and the slower information where AI adds value is already handled by the daily-cycle analysis.

The case for running it continuously was weak and the cost wasn't small — final call. Intraday keeps only rule-based detection now, with AI narration called on demand only when needed.

Wrapping up

Looking back, this was a week spent standing at the threshold of live trading, pulling out everything untrustworthy before crossing it.

Data quietly missing, a signal that existed but nobody read, a safeguard that passed through exactly when it mattered most — all things that don't show themselves until it's too late. And right at the threshold where real money is on the line, those invisible things are the most expensive ones.

So the conclusion for the week is simple: doubt before crossing, and don't just fix the same mistake — make sure it can't happen again.