Building a 3-Layer Safety Net for My Trading Bot | Toss Dev #0

I’m building an auto-trading bot on top of a Korean brokerage’s Open API (I’ll just call it “the broker” here — it’s a mainstream Korean stock trading platform with a REST API). Before touching real orders, I wanted to make sure authentication and market data were rock solid. That’s what Phase 0 was about.

Splitting into five files

I started with everything in one script. That fell apart fast, so I split it by responsibility.

FileRoleKey contents
toss_common.pyShared module.env-based credential loading, OAuth2 token issuance, account number lookup, single/batch price quotes, a unified auth function, dual console+file logging
get_price.pyPrice fetcherBatch current-price lookup for a watchlist, printed to console
toss_portfolio.pyPortfolio dashboardHoldings lookup, plus width-aware alignment for mixed Korean/English text using unicodedata.east_asian_width()
toss_test.pyConnection diagnostics3-stage smoke test: auth → auth again → raw holdings response
auto_trader.pyTrading enginePrice-watching loop, a triple safety net, a Telegram-notification skeleton, and retry/backoff for expired tokens and network errors

The shared module came first

I didn’t want to copy-paste auth logic across every file, so toss_common.py handles credentials, token issuance, account lookup, and price quotes in one place. Every other file just calls authenticate(). Logging to both console and file turned out to be more useful than expected once I started chasing an intermittent 401.

Korean text alignment was more annoying than it should have been

toss_portfolio.py prints holdings in a table, and mixing Korean stock names with English/numeric fields wrecked the column alignment. Korean characters render at roughly double the width of Latin characters, and plain string-length math doesn’t know that. I ended up using unicodedata.east_asian_width() to compute actual display width, wrapped into three helpers — get_display_width, fit_to_width, and center_korean. Felt like overkill for console output, but the table is genuinely readable now.

The triple safety net — non-negotiable

A trading bot with a bug can lose real money fast, so auto_trader.py ships with three independent layers before it, so it never places a live order in this phase.

DRY_RUN = True  # no live orders, period
TARGET_BUY_PRICE = 1  # placeholder target price, effectively unfillable

# the live order call is physically commented out
# response = requests.post(ORDER_URL, headers=headers, json=order_payload)

I didn’t trust a single DRY_RUN flag. The target price is set low enough that a buy condition can’t realistically be met, and the actual order-placing call is commented out in the source. All three would have to fail at once for a real order to go through. Might be overkill, but I’d rather over-engineer the safety net than find out the hard way.

Retry logic for token expiry and network hiccups

Like most OAuth2 APIs, access tokens expire after a while. Instead of crashing on a 401, the bot re-issues a token and retries the original request once. Network blips get a few retries with exponential backoff too. I haven’t run this for long stretches yet, so I’ll be watching how well it holds up in the next phase.

What’s next

toss_test.py passes all three diagnostic stages. Next phase, I want to run the watch loop for longer stretches to stress-test the retry logic, and turn the Telegram notification skeleton into something that actually fires.

15 thoughts on “Building a 3-Layer Safety Net for My Trading Bot | Toss Dev #0”

  1. Pingback: [Toss Auto-Trading Dev Journal #0-1] The Case of the Vanishing Overseas Holdings - Orbit - Space & ETF Investing

  2. Pingback: [Toss Auto-Trading Dev Journal #3] Phase 1: Centralizing Config, Unifying Retry Logic, and a Regression Scare - Orbit - Space & ETF Investing

  3. Pingback: [Toss Auto-Trading Dev Journal #4] Centralizing Config, Unifying Retry Logic, and a Regression Scare - Orbit - Space & ETF Investing

  4. Pingback: [Toss Auto-Trading Dev Journal #5] Adding pytest: Turning a Regression Scare Into a Safety Net - Orbit - Space & ETF Investing

  5. Pingback: [Toss Auto-Trading Dev Journal #6] Taming Infinite Log Growth — Wrapping Up Phase 1 - Orbit - Space & ETF Investing

  6. Pingback: [Toss Auto-Trading Dev Journal #7] Extracting the Buy Condition into a Strategy Class (Phase 2 Kickoff) - Orbit - Space & ETF Investing

  7. Pingback: [Toss Auto-Trading Dev Journal #8] From One Symbol to Many - Orbit - Space & ETF Investing

  8. Pingback: [Toss Auto-Trading Dev Journal #9] Why I Locked Down the Data Persistence Design Before Writing a Single Line - Orbit - Space & ETF Investing

  9. Pingback: [Toss Auto-Trading Dev Journal #10] Building the SQLite Persistence Module - Orbit - Space & ETF Investing

  10. Pingback: Wiring the Trading Engine Into SQLite Logging | Toss Auto-Trading Dev Journal #11 (Phase 3-3) - Orbit - Space & ETF Investing

  11. Pingback: Plugin Interface for Trading Strategies | Toss Auto-Trading Dev Journal #12 (Phase 2-3) - Orbit - Space & ETF Investing

  12. Pingback: Backtesting on Just a Price List | Toss Auto-Trading Dev Journal #13 (Phase 4) - Orbit - Space & ETF Investing

  13. Pingback: Candle API Pagination for Historical Backtesting | Toss Auto-Trading Dev Journal #14 - Orbit - Space & ETF Investing

  14. Pingback: File-Based Signals for Bot Pause Control | Toss Auto-Trading Dev Journal #16 - Orbit - Space & ETF Investing

  15. Pingback: Wiring Up python-telegram-bot Command Handlers | Toss Auto-Trading Dev Journal #17 - Orbit - Space & ETF Investing

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top