Building an Order Quantity Guardrail Before Going Live | Toss Auto-Trading Dev Journal #19

🌐 ν•œκ΅­μ–΄λ‘œ 보기

πŸ’‘ Phase 7 Summary

  • Goal: Add a guardrail preventing unintentionally large order sizes caused by bugs or config mistakes
  • Key decision: Inserted as a “layer 0” check that runs before DRY_RUN, so even simulation mode catches oversized orders
  • Verification: 4 new tests added, full pytest suite passes at 122/122 (118 existing + 4 new)

From Phase 0 to Phase 7: Getting Ready to Go Live

Phases 0 through 6 built up the bot piece by piece β€” config separation, a strategy class, database persistence, a backtest engine, Telegram control, a scheduler with a watchdog. The last item on the roadmap is Phase 7: getting ready for live trading, starting with a new order quantity guardrail.

That name alone makes this phase feel heavier than the rest. Everything so far has run safely inside DRY_RUN mode, but once Phase 7 wraps up, real orders could eventually hit a real account. So instead of flipping the safety switches off right away, I decided to work through a short checklist of things worth preparing first.

The pre-live checklist

  1. Order quantity guardrail (today’s topic)
  2. Daily/cumulative loss limit with auto-pause
  3. Backtest verification with real target prices and strategy
  4. (after explicit approval) Gradual removal of the three-layer safety net

Given usage limits, I’m tackling one item per day. Today: the order quantity guardrail.

The Problem: Nothing Stopped an Oversized Order

Going back through the code, one gap stood out. A bug in quantity calculation, or a config typo, could send an unintentionally large order to the market. The existing three-layer safety net (DRY_RUN, a 1-won target price, and physically commented-out order API calls) blocks trades themselves, but it doesn’t catch a logic mistake in the code.

So I added a new setting, MAX_ORDER_QUANTITY_PER_TRADE, to config.py, defaulting to 10 shares. Any single order exceeding that gets rejected outright β€” this is the order quantity guardrail at the center of today’s post.

Design: Checking This Before DRY_RUN

The key design decision here was where in the pipeline this order quantity guardrail runs. I inserted it as a “layer 0” check that fires before the existing three-layer safety net, inside execute_order().

Why does the ordering matter? Because if the guardrail rejects an oversized order even while DRY_RUN=True, I can catch quantity-calculation bugs during simulation β€” well before anything goes live, instead of discovering them right before flipping the safety switches off.

Using a masked example (Samsung Electronics, ticker 005930): an order meant to buy 5 shares that accidentally becomes 500 shares due to a calculation bug gets caught right here, at layer 0.

Note: an order that exceeds the limit only triggers an ERROR-level alert β€” it’s never written into the trade history. That keeps trades that never actually executed from contaminating P&L statistics later on.

Verification: 4 New Tests, 122/122 Passing

I added three tests to test_auto_trader.py:

  • An oversized order gets rejected
  • An order exactly at the limit (e.g. 10 shares) still executes normally
  • A rejected order never appears in the trade history

I also added a regression test to test_config.py confirming the limit itself stays within a sane range (1–100). If that value ever gets accidentally bumped way too high, the whole guardrail becomes meaningless β€” this test exists to catch that mistake before it happens.

Running the full pytest suite afterward confirmed all 122 tests passing (118 existing + 4 new), and I double-checked that the existing DRY_RUN and TARGET_BUY_PRICE safety values were untouched.

Result: A Four-Layer Safety Net

LayerWhat it does
0 (new)Rejects any order exceeding the quantity limit
1DRY_RUN=True simulation mode
2Target price set to 1 won (unfillable)
3Order API physically commented out

Both the local environment and the project repo are synced and confirmed.

What’s Next: Auto-Pause on Loss Limits

Next up is a guardrail that auto-pauses the monitoring loop once daily or cumulative losses cross a threshold, reusing the engine_control.pause() method built back in Phase 5. After that comes backtest verification with real target prices and strategy, and finally β€” once explicitly approved β€” the gradual removal of the three-layer safety net.

πŸ‘‰ Next up: an auto-pause guardrail for daily and cumulative loss limits.

FAQ

Q1. Why does the order quantity guardrail run before DRY_RUN?

Rejecting oversized orders even in simulation mode means quantity-calculation bugs get caught before going live, not discovered right before the safety switches come off.

Q2. Does a rejected order show up in trade history?

No β€” it only triggers an ERROR-level alert and is never written to the trade history, so failed simulated trades don’t skew P&L statistics.

Q3. What’s the default order limit?

MAX_ORDER_QUANTITY_PER_TRADE defaults to 10 shares and is configurable.

Q4. What’s the next Phase about?

An auto-pause guardrail that halts the monitoring loop once daily or cumulative losses exceed a set threshold.

5 thoughts on “Building an Order Quantity Guardrail Before Going Live | Toss Auto-Trading Dev Journal #19”

  1. Pingback: 1회 μ£Όλ¬Έ ν•œλ„ λ°©μ–΄ 둜직으둜 μ•ˆμ „μž₯치 κ°•ν™”ν•˜κΈ° | ν† μŠ€ μžλ™λ§€λ§€ 개발기 #19 - TS ν€€νŠΈ: 주식 μžλ™λ§€λ§€ μ—°κ΅¬μ†Œ

  2. Pingback: Auto-Pausing on Cumulative Loss Limit | Toss Auto-Trading Dev Journal #20 - Orbit - Space & ETF Investing

  3. Pingback: Toss Auto-Trading Bot: Take-Profit/Stop-Loss Cycle Complete #23 - Orbit - Space & ETF Investing

  4. Pingback: Trading Bot Dashboard with FastAPI | Toss Dev Journal #24 - Orbit - Space & ETF Investing

  5. Pingback: Toss Securities Holdings Dashboard | Dev Journal #25 - Orbit - Space & ETF Investing

Leave a Comment

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

Scroll to Top