The Symptom
I noticed something weird. My backtest showed entries throughout the day, but the first batch of trades always started around 9 AM KST.
For the first 9 hours? Nothing. Zero entries. Every single day.
My live bot was trading fine during those hours. But my backtest was blind to them.
The Hunt
I checked everything:
- Entry conditions? Working.
- Data feed? Complete.
- Candle timestamps? Looked correct.
- RSI warmup? Sufficient.
Everything looked fine individually. The bug was in the interaction between two correct-looking pieces of code.
The Bug
|
|
I was passing a “naive” datetime (no timezone info) to an API that expected UTC. My local time is KST (UTC+9).
So when I said “start at midnight,” the exchange heard “start at midnight UTC,” which is 9 AM in Korea.
Nine hours of trades, invisible.
Why It Was Hard to Find
The timestamps in my logs looked correct because I was formatting them in local time. The data was correct — just starting 9 hours late. And since I usually analyzed data from “yesterday” (a full day), the missing morning hours weren’t obvious.
It only became visible when I compared live trades against the backtest hour by hour.
The Fix
|
|
And for display, always convert back to KST:
|
|
One rule: all internal times in UTC, all display times in KST. No exceptions.
The Deeper Problem
This bug existed because I was sloppy about timezones from the start. I mixed:
datetime.now()(local KST) in the live bot- UTC timestamps from the exchange API
- Naive datetimes in the backtest
Every combination of two worked fine. All three together created a 9-hour ghost.
Timezone Rules for Trading Bots
After this disaster, I follow these rules:
-
Pick one internal timezone and stick with it. UTC is standard, but whatever you pick, be consistent.
-
Never use naive datetimes. Always attach timezone info, or at minimum, document which timezone every variable uses.
-
Label your outputs. Every timestamp in logs should say
(KST)or(UTC). Future you will thank present you. -
Test across timezone boundaries. If your backtest starts at midnight, does it start at YOUR midnight or the exchange’s midnight?
-
Compare live vs backtest hourly. Daily totals can mask timezone offsets that cancel out.
The Cost
This wasn’t just a code quality issue. Those 9 missing hours in the backtest meant:
- Optimization results were wrong (trained on partial data)
- Strategy comparisons were skewed
- I couldn’t verify live trades against backtest for morning sessions
A one-line bug that invalidated weeks of analysis.
The scariest bugs aren’t the ones that crash your program. They’re the ones that silently give you wrong answers.