Toss Auto-Trading Dev Journal Series (Current: #0-1 / 2 posts total)
- Phase 0 — Foundation
- Phase 0-1 — Currency bug fix (this post)
Previous: [Phase 0] Five Files, One Bot
A few days into running the portfolio dashboard, I noticed something off. The per-holding list correctly showed overseas positions (dummy tickers “SpaceX” and SOXL) in dollars — but the top-line summary stats (total purchase amount, market value, total P&L) didn’t reflect any of it. Individual rows were right, the totals were wrong. That mismatch is always a bad sign.
The cause: a hardcoded currency key
I went back to the raw API response via toss_test.py. Fields like totalPurchaseAmount, marketValue.amount, profitLoss.amount, and dailyProfitLoss.amount all came back split by currency, as {"krw": ..., "usd": ...}. But my summary logic only read .get("krw") — the USD side was being silently discarded. I also found a second landmine: every one of these amounts comes back as a string, not a number (e.g. "1687167"). Skipping the cast would’ve caused a different bug down the line.
Worth noting: profitLoss.rate and dailyProfitLoss.rate only come back as a single blended percentage, with no currency split.
I chose separate currency totals over FX conversion
Two options were on the table:
- Option A: show KRW and USD totals separately, no conversion
- Option B: pull in an FX rate and show one blended total
Option B looks more “finished,” but any FX error would make a number that’s supposed to be precise — my actual asset total — less accurate, not more. It also meant a lot more scope. I went with A: showing the real numbers without distortion mattered more than a single combined figure.
def extract_amounts_by_currency(amount_dict: dict) -> dict:
"""Walk whatever currency keys the response actually contains, casting safely"""
result = {}
for currency, value in amount_dict.items():
try:
result[currency] = float(value)
except (TypeError, ValueError):
result[currency] = 0.0
return result
Instead of hardcoding currency keys, this walks whatever’s actually in the response — so if a third currency shows up later, nothing needs to change. The dashboard now prints separate [KRW] and [USD] blocks, and the USD block disappears automatically for accounts with no overseas holdings.
Then the real mess started
After the fix, running the script produced… nothing. No error, no output, just back to the prompt. Turned out the entire if __name__ == "__main__": entry point block had vanished somewhere while I was moving code around.
I restored it and ran again — this time, NameError: name 'logger' is not defined. The top-of-file imports and a few helper functions (safe_int, get_display_width) had gone missing along with it. Patching piece by piece had left the file in a half-broken state.
The missing overseas totals came from a hardcoded KRW-only key, fixed with a currency-agnostic helper. Along the way, partial patching caused the entry point and several helper functions to disappear from the file.
That’s when I stopped trying to patch fragments and rebuilt the whole file as one complete unit instead. Partial patches feel faster in the moment, but tracking down what silently went missing costs a lot more time than just doing it properly once.
The result
After the full rebuild, KRW and USD totals both came out correctly.
💰 [KRW] Total Purchase: ₩1,687,167
💵 [USD] Total Purchase: $1,716.08
Exactly what I was expecting to see.
What this connects to next
The “partial patches fragment your code” lesson here lines up with the retry/backoff consolidation work planned for Phase 1. Consolidating scattered logic into one place is starting to feel less optional.
Pingback: [Toss Auto-Trading Dev Journal #4] Centralizing Config, Unifying Retry Logic, and a Regression Scare - Orbit - Space & ETF Investing
Pingback: [Toss Auto-Trading Dev Journal #5] Adding pytest: Turning a Regression Scare Into a Safety Net - Orbit - Space & ETF Investing
Pingback: [Toss Auto-Trading Dev Journal #6] Taming Infinite Log Growth — Wrapping Up Phase 1 - Orbit - Space & ETF Investing
Pingback: [Toss Auto-Trading Dev Journal #8] From One Symbol to Many - Orbit - Space & ETF Investing
Pingback: [Toss Auto-Trading Dev Journal #10] Building the SQLite Persistence Module - Orbit - Space & ETF Investing
Pingback: Designing a SQLite Schema for Trade Persistence | Toss Dev #3-1 - Orbit - Space & ETF Investing
Pingback: Abstracting Strategy Classes for Cleaner Trading Logic | Toss Dev #2-1 - Orbit - Space & ETF Investing
Pingback: Wiring the Trading Engine Into SQLite Logging | Toss Auto-Trading Dev Journal #11 (Phase 3-3) - Orbit - Space & ETF Investing
Pingback: Plugin Interface for Trading Strategies | Toss Auto-Trading Dev Journal #12 (Phase 2-3) - Orbit - Space & ETF Investing
Pingback: Backtesting on Just a Price List | Toss Auto-Trading Dev Journal #13 (Phase 4) - Orbit - Space & ETF Investing
Pingback: Candle API Pagination for Historical Backtesting | Toss Auto-Trading Dev Journal #14 - Orbit - Space & ETF Investing