๐ Toss Auto-Trading Dev Journal Series (currently Phase 2-1 ยท #7 of the series)
- #0 Foundation system (5-file architecture, triple safety net)
- #0-1 Fixing a currency bug in overseas holdings
- #3 Centralizing config + a retry decorator
- #4 Config/retry follow-up and a regression
- #5 Unit testing with pytest (33 passing)
- #6 Log rotation with RotatingFileHandler
- #7 Extracting the buy condition into a strategy class (this post)
๐ Previous: #6 Log rotation with RotatingFileHandler
Quick context if you’re new here: this is a personal Python trading bot built on the API for Toss Securities, a Korean brokerage. After wrapping up Phase 1 (config centralization, a retry decorator, pytest coverage, log rotation), the roadmap’s next item was strategy modularization.
Up to this point, the buy condition โ “buy if current price โค target price” โ was just an if statement hardcoded inside the monitoring loop in auto_trader.py. Fine for one rule, but adding a second condition (a moving average, RSI, whatever) later would mean touching that loop again every time.
Why bother now
I don’t have an immediate plan to add a moving-average strategy, but the structural debt bothered me โ needing to open the loop code every time a new rule shows up isn’t sustainable. So I split Phase 2 into three steps:
- โ Abstract the existing condition into a class
- โก Design a plugin-style strategy interface
- โข Support monitoring multiple symbols instead of one
Doing all three at once seemed like a good way to make verification messy, so this session was scoped to just step โ . The safety net (DRY_RUN, TARGET_BUY_PRICE) stayed completely untouched.
New strategy.py โ the ThresholdBuyStrategy class
I created a new file, strategy.py, with a ThresholdBuyStrategy class, and moved the existing condition into a should_buy(current_price) method. Nothing about the value or the logic changed โ just how it’s expressed, which kept the risk low.
class ThresholdBuyStrategy:
def __init__(self, target_price: float):
self.target_price = target_price
def should_buy(self, current_price: float) -> bool:
return current_price <= self.target_price
(Sample code โ the actual ticker and target price are masked with a placeholder like Samsung Electronics, 005930, and an arbitrary amount.)
The hardcoded if current_price <= TARGET_BUY_PRICE: inside the loop in auto_trader.py became a single call: if buy_strategy.should_buy(current_price):. The target price is still injected from TARGET_BUY_PRICE in config.py, unchanged.
Verification โ 38 passing tests
I added test_strategy.py with four new tests: the normal case, the boundary case (current price exactly equal to target), and a scenario using the real safety-net value (a 1-won target). Running the full suite again: 34 existing + 4 new = 38 passing tests.
I didn’t stop at tests passing โ I also ran python auto_trader.py briefly to confirm it still polled every 5 seconds as before, correctly generated no buy signal (since the target is set to 1 won), and shut down cleanly on Ctrl+C.
A side note โ project storage overwrite behavior
While syncing the modified files into project storage, I wanted to know whether re-uploading a file with the same name would automatically overwrite the old one. It doesn’t โ project attachments on this platform are read-only, so re-uploading under the same name can create a duplicate instead of overwriting. I switched to deleting the old file first, then re-uploading.
Extracting a trading bot’s buy condition into a ThresholdBuyStrategy class means future strategies โ like moving averages or RSI โ can be added without touching the core monitoring loop.
What’s next
Two items remain in Phase 2. Next up is multi-symbol monitoring support, followed by designing a plugin interface for indicator-based strategies like moving averages or RSI.
FAQ
Does moving the logic into a class hurt performance?
No โ it’s one extra method call, negligible against a 5-second polling loop.
Did the safety net change?
No. This was purely a refactor of how the logic is expressed; the values and behavior are untouched.
Why not design the plugin interface in the same session?
Designing the interface upfront would have expanded the scope needing verification. Keeping this session to just the class abstraction kept the risk contained.
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: [Toss Auto-Trading Dev Journal #10] Building the SQLite Persistence Module - Orbit - Space & ETF Investing
Pingback: Plugin Interface for Trading Strategies | Toss Auto-Trading Dev Journal #12 (Phase 2-3) - Orbit - Space & ETF Investing
Pingback: Wiring Up python-telegram-bot Command Handlers | Toss Auto-Trading Dev Journal #17 - Orbit - Space & ETF Investing