π νκ΅μ΄λ‘ 보기
π Toss Auto-Trading Dev Journal (Phase 2 / 12 posts so far)
- Phase 0 β Foundation (#0)
- Phase 0-1 β Currency unit bug (#0-1)
- Phase 1 β Config split & retry logic (#3)
- Phase 1-2 β Config/retry follow-up (#4)
- Phase 1-3 β Adding pytest (#5)
- Phase 1-4 β Log rotation (#6)
- Phase 2-1 β Strategy class abstraction (#7)
- Phase 2-2 β Multi-symbol monitoring (#8)
- Phase 2-3 β A plugin interface for strategies (this post, #12)
- Phase 3-1 β SQLite schema design (#9)
- Phase 3-2 β db.py implementation (#10)
- Phase 3-3 β Wiring it into the engine (#11)
β Previous: Phase 2-2: Multi-symbol monitoring | Next β: Phase 3-1: SQLite schema design
π‘ Phase 2-3 at a glance
- Goal: Stop creating ad-hoc classes every time a new strategy shows up, and close out Phase 2 (strategy modularization) with a proper plugin interface
- Key decision: An abstract class
TradingStrategy(inheriting fromABC) defines one shared contract,should_buy(current_price); the existingThresholdBuyStrategynow implements it with zero behavior change - Verification: interface-compliance tests plus 8 new tests for a demo
MovingAverageStrategy, full pytest suite at 46 passed
For readers new to this series: this bot trades through Toss Securities’ brokerage API β think of it as a Korean-market equivalent of Alpaca or Interactive Brokers. The first two items in Phase 2 were already done: a standalone strategy.py module, and support for watching several symbols at once. The last piece was making room for future strategies β moving averages, RSI, whatever comes next β without everyone reinventing the class structure each time.
Defining a shared contract with ABC
I built TradingStrategy on top of Python’s abc.ABC, with exactly one abstract method: should_buy(current_price). Implement that one method, and a strategy can plug straight into the engine.
from abc import ABC, abstractmethod
from collections import deque
class TradingStrategy(ABC):
@abstractmethod
def should_buy(self, current_price: float) -> bool:
...
Refactoring ThresholdBuyStrategy β behavior untouched
The existing ThresholdBuyStrategy now inherits from TradingStrategy. The one rule I held myself to: not a single line of the actual buy logic changes. It still fires when the current price (against a placeholder symbol like 005930) drops to or below the target β only the inheritance changed.
A demo MovingAverageStrategy
To prove the interface actually generalizes, I added a demo MovingAverageStrategy based on a simple moving average. It uses deque(maxlen=window) so old prices past the window size fall off automatically.
class MovingAverageStrategy(TradingStrategy):
def __init__(self, window: int = 5):
self.window = window
self.prices = deque(maxlen=window)
def should_buy(self, current_price: float) -> bool:
self.prices.append(current_price)
if len(self.prices) < self.window:
return False
avg = sum(self.prices) / len(self.prices)
return current_price < avg
A trading-strategy plugin interface means defining an abstract class like TradingStrategy with Python’s ABC, so any strategy that implements one shared method β should_buy(current_price) β can plug into the engine the same way, whether it’s ThresholdBuyStrategy or MovingAverageStrategy.
This demo strategy is strictly for proving the interface β its parameters haven’t been validated by backtesting (that’s Phase 4), so I kept the live monitoring loop in auto_trader.py pointed at the already-verified ThresholdBuyStrategy only.
Verification: interface compliance plus behavior
I added two kinds of tests to test_strategy.py: one confirming TradingStrategy can’t be instantiated directly and that both implementations subclass it correctly, and eight more checking that MovingAverageStrategy holds off when there isn’t enough data yet, fires correctly above/below the moving average, and that deque properly drops old prices once the window fills up. Re-running the full suite gave the existing 38 plus these 8 β 46 passed β and I double-checked that auto_trader.py‘s safety values, DRY_RUN=True and TARGET_BUY_PRICE=1, hadn’t moved.
FAQ
Q. Why not build a full indicator-based strategy in the same pass?
Bundling in a real strategy with heavier calculation logic would’ve made verification more complex and riskier, so I scoped this session to the interface design only.
Q. Does the demo strategy ever trade for real?
No β auto_trader.py still only runs the verified ThresholdBuyStrategy; MovingAverageStrategy exists purely to prove the interface works.
Q. What does adding a new strategy look like now?
Subclass TradingStrategy, implement should_buy(current_price), and it drops right into the existing monitoring loop.
Pingback: μ λ΅ νλ¬κ·ΈμΈ μΈν°νμ΄μ€ μ€κ³, ABCλ‘ μ 리νκΈ° | ν μ€ μλλ§€λ§€ κ°λ°κΈ° #12 (Phase 2-3) - TS ννΈ: μ£Όμ μλλ§€λ§€ μ°κ΅¬μ
Pingback: Candle API Pagination for Historical Backtesting | Toss Auto-Trading Dev Journal #14 - Orbit - Space & ETF Investing