전략 플러그인 인터페이스 설계, ABC로 정리하기 | 토스 자동매매 개발기 #12 (Phase 2-3)

🌐 Read in English

📚 토스 자동매매 개발기 시리즈 (Phase 2 / 전체 12편)

  • Phase 0 — 기반 시스템 구축 (#0)
  • Phase 0-1 — 통화 단위 버그 수정 (#0-1)
  • Phase 1 — 설정 분리 & 재시도 로직 (#3)
  • Phase 1-2 — 설정/재시도 후속 보완 (#4)
  • Phase 1-3 — pytest 테스트 도입 (#5)
  • Phase 1-4 — 로그 로테이션 (#6)
  • Phase 2-1 — 전략 클래스 추상화 (#7)
  • Phase 2-2 — 복수 종목 감시 (#8)
  • Phase 2-3 — 전략 플러그인 인터페이스 설계 (이 글, #12)
  • Phase 3-1 — SQLite 스키마 설계 (#9)
  • Phase 3-2 — db.py 모듈 구현 (#10)
  • Phase 3-3 — DB 실전 연동 (#11)

← 이전 글: Phase 2-2: 복수 종목 감시 | 다음 글 →: Phase 3-1: SQLite 스키마 설계

💡 이번 Phase 2-3 요약

  • 목적: 새 매매 전략을 추가할 때마다 임의로 클래스를 만들던 구조를, 공통 규격을 가진 플러그인 형태로 정리해 Phase 2(전략 모듈화)를 완전히 마무리
  • 핵심 포인트: ABC를 상속한 추상 클래스 TradingStrategyshould_buy(current_price) 공통 규격을 정의하고, 기존 ThresholdBuyStrategy는 동작 변경 없이 이 인터페이스만 상속하도록 리팩터링
  • 검증: 인터페이스 준수 테스트 + 데모용 MovingAverageStrategy 동작 테스트 8개 추가, pytest 전체 46개 통과

Phase 2의 앞선 두 항목은 이미 끝난 상태였다. strategy.py를 새로 만들었고, 종목 여러 개를 동시에 감시하는 기능도 붙였다. 남은 건 마지막 하나, “이동평균이나 RSI 같은 지표 기반 전략을 나중에 플러그인처럼 추가할 수 있게 만들기”였다. 그런데 막상 시작하려니 ThresholdBuyStrategy 하나만 덩그러니 있는 상태에서, 다음에 전략을 하나 더 추가할 때 또 각자 알아서 클래스를 만들면 규격이 제각각이 될 게 뻔했다.

ABC로 공통 규격 정의하기

파이썬 표준 라이브러리의 abc.ABC를 상속받아 TradingStrategy 추상 클래스를 만들었다. 여기에 should_buy(current_price)라는 메서드 하나만 @abstractmethod로 정의했다. 이 메서드만 구현하면 어떤 전략이든 엔진에 그대로 꽂을 수 있게 만드는 게 목표였다.

from abc import ABC, abstractmethod
from collections import deque

class TradingStrategy(ABC):
    @abstractmethod
    def should_buy(self, current_price: float) -> bool:
        ...

ThresholdBuyStrategy 리팩터링 — 동작은 그대로

기존 ThresholdBuyStrategy가 이 인터페이스를 상속하도록 바꿨다. 여기서 제일 신경 쓴 부분은 “로직은 단 한 줄도 안 바꾼다”는 원칙이었다. 목표가(가상 종목 005930 기준 예시가) 이하일 때 매수 신호를 내는 기존 판단 로직은 그대로 두고, 클래스 상속 관계만 추가했다.

데모용 MovingAverageStrategy 추가

인터페이스가 실제로 다른 종류 전략에도 적용되는지 보여주기 위해 단순이동평균 기반의 MovingAverageStrategy를 데모로 만들었다. deque(maxlen=window)를 써서 윈도우 크기를 넘어가는 오래된 시세는 자동으로 빠지게 했다.

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

이 데모 전략은 어디까지나 인터페이스 검증용이라, 실전 감시 루프인 auto_trader.py는 여전히 검증된 ThresholdBuyStrategy만 사용하도록 그대로 뒀다. 파라미터가 백테스팅(Phase 4)으로 검증되지 않은 예시 구현을 실전에 바로 연결하는 건 위험하다고 판단했기 때문이다.

검증: 인터페이스 준수 + 동작 테스트

test_strategy.py에 두 갈래로 테스트를 추가했다. 하나는 TradingStrategy를 직접 인스턴스화할 수 없는지, 두 구현체 모두 이 인터페이스를 상속하는지 확인하는 테스트. 다른 하나는 MovingAverageStrategy가 데이터 부족 시 판단을 보류하고, 이동평균 상회/하회에 따라 신호를 제대로 내고, deque가 윈도우를 넘는 오래된 데이터를 자동으로 밀어내는지 확인하는 8개 케이스였다. 전체 pytest를 다시 돌려서 기존 38개 + 신규 8개, 46 passed를 확인했고, auto_trader.pyDRY_RUN=True, TARGET_BUY_PRICE=1 안전장치 값도 그대로인지 재확인했다.

자주 묻는 질문

Q. 왜 지표 계산 로직이 있는 실전 전략까지 한 번에 안 넣었나요?
계산 로직이 큰 실전 전략까지 이번에 다 넣으면 검증이 복잡해지고 리스크가 커진다고 판단해서, 이번엔 인터페이스 설계로 범위를 한정했다.

Q. 데모 전략도 실제 매매에 쓰이나요?
아니다. auto_trader.py는 여전히 검증된 ThresholdBuyStrategy만 사용하고, MovingAverageStrategy는 인터페이스 검증용 예시로만 남겨뒀다.

Q. 새 전략을 또 추가하려면 뭘 하면 되나요?
TradingStrategy를 상속해서 should_buy(current_price)만 구현하면 기존 감시 로직에 그대로 꽂을 수 있다.

“전략 플러그인 인터페이스 설계, ABC로 정리하기 | 토스 자동매매 개발기 #12 (Phase 2-3)”에 대한 1개의 생각

댓글 남기기