A day after splitting out the buy-decision logic into strategy.py, the next roadmap item was up: teaching the bot to watch more than one stock at a time.
Toss Auto-Trading Dev Journal series (entry 8/8)
- #0 Foundation system (5-file architecture, triple safety net)
- #0-1 Fixing the overseas-holdings currency bug
- #3 Config centralization + retry logic
- #4 Config/retry follow-up and a regression incident
- #5 Adding 33 pytest unit tests
- #6 Log rotation with RotatingFileHandler
- #7 Strategy class abstraction (strategy.py)
- #8 (this post) Multi-symbol monitoring support
← Previous: #7 Strategy Class Abstraction | Next: not yet published
The Problem: A Bot That Could Only Watch One Stock
Toss Securities is a Korean brokerage, and its API — like most retail brokerage APIs — returns prices in Korean won and expects a single account/token session per app. My auto_trader.py was built around a single TARGET_SYMBOL string in config.py. That was fine until it wasn’t: if consecutive errors crossed a threshold, the whole engine shut down, because the original design assumed there was only ever one symbol to lose.
The Redesign: TARGET_SYMBOL Becomes TARGET_SYMBOLS
TARGET_SYMBOL (a single string) became TARGET_SYMBOLS (a list). Each symbol now gets its own ThresholdBuyStrategy instance and its own consecutive-error counter, so one symbol’s problems can’t bleed into another’s.
The bigger change was the failure policy itself. Previously, hitting the error limit killed the whole engine. Now, only the offending symbol gets dropped from the watch list — everything else keeps running. That’s a real availability win, but it comes with a tradeoff: a symbol can silently drop out and nothing screams about it yet. Alerting is on the list for later.
Token expiry stayed a shared, account-level concern rather than a per-symbol one — the moment it’s detected, the bot re-authenticates and re-fetches every symbol.
class SymbolWatcher:
def __init__(self, symbol, strategy, max_error_count=5):
self.symbol = symbol
self.strategy = strategy
self.error_count = 0
self.max_error_count = max_error_count
self.active = True
def handle_error(self):
self.error_count += 1
if self.error_count >= self.max_error_count:
self.active = False # only this symbol drops out
For testing, I registered four placeholder tickers — Samsung Electronics (005930), SK Hynix (000660), Kakao (035720), and Naver (035420). These are illustrative examples, not actual holdings.
Validation: Mock Simulation, Then a Real Local Run
First pass was a mocked simulation with no real API calls — three scenarios (“buys immediately,” “keeps failing on network errors and gets dropped,” “watches normally and eventually buys”) run concurrently across placeholder symbols, confirming they didn’t interfere with each other.
Second pass was an actual local run: four placeholder symbols registered, auto_trader.py running for real, cycling through all four every 5 seconds as expected.
All 38 existing pytest tests passed with no regressions, and safety-net values like DRY_RUN=True and TARGET_BUY_PRICE=1 were untouched.
The Gotcha: TARGET_STOCKS vs. TARGET_SYMBOLS
After bumping the symbol list to four entries, the bot still only watched one. Turned out config.py had two similarly-named variables: TARGET_STOCKS, used only by a separate price-lookup script (get_price.py), and TARGET_SYMBOLS, the one auto_trader.py actually reads. The wrong one had been edited.
Fixing the right variable solved it immediately. The names were close enough to cause real confusion, so renaming one of them for clarity is now a roadmap note for later.
To monitor multiple symbols with a Toss Securities API trading bot, turn the single-symbol config variable into a list and give each symbol its own strategy instance and error counter.
What’s Next: A Plugin Interface for Strategies
The last item in Phase 2 is designing a plugin-style interface so indicator-based strategies (moving averages, RSI, etc.) can be added without touching the core engine. Having per-symbol strategy instances already in place should make that transition smoother.
FAQ
What happens if one symbol keeps failing while others are fine?
Only the symbol that crosses the error threshold gets dropped from the watch list. The rest keep monitoring normally — the engine no longer shuts down entirely.
Can different symbols run different buy strategies?
Structurally yes, since each symbol gets its own strategy instance. This round only validated running the same strategy across multiple symbols in parallel.
Is token expiry handled per symbol too?
No — it’s treated as an account-wide issue. As soon as it’s detected, the bot re-authenticates and re-fetches all symbols.
Pingback: [Toss Auto-Trading Dev Journal #9] Why I Locked Down the Data Persistence Design Before Writing a Single Line - 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: 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