백테스트로 전략 평가하기#

백테스팅 시스템 컴포넌트 개요#

pyqqq.backtest 모듈은 다음과 같은 주요 컴포넌트들로 구성되어 있습니다:

핵심 컴포넌트#

  • Environment: 트레이딩 시스템의 실행 환경을 정의

    • BacktestEnvironment: 백테스팅을 위한 가상 환경

    • KISDomesticEnvironment: 국내주식 실거래 환경

    • KISOverseasEnvironment: 해외주식 실거래 환경

  • Broker: 주문 처리와 시장 데이터 조회를 담당

    • MockBroker: 백테스팅용 가상 브로커

    • TradingBroker: 실제 거래 실행 브로커

  • PositionProvider: 포지션 정보 관리

    • ManualPositionProvider: 수동 포지션 관리

    • BackPositionProvider: DB 기반 포지션 관리

    • KISPositionProvider: 실시간 포지션 조회

  • WallClock: 시스템의 시간 관리

    • 실시간 모드: 실제 시간 기준 동작

    • 백테스트 모드: 가상 시간 기준 동작

  • Strategy: 실제 트레이딩 로직 구현

    • BaseStrategy: 모든 전략의 기본 클래스

컴포넌트 간 관계#

Environment
├── WallClock (시간 관리)
└── Broker (주문 처리)
    └── PositionProvider (포지션 관리)

Strategy
├── Environment 참조
└── 실제 매매 로직 구현

Step 1: 전략 구현 및 백테스팅#

1.1 전략 코드 작성#

5분 이동평선을 돌파하면 매수하고, %3 수익이 나면 매도하는 간단한 전략을 구현합니다.

# strategy.py
from pyqqq.backtest.environment import BacktestEnvironment
from pyqqq.backtest.strategy import BaseStrategy
from pyqqq.datatypes import OrderSide, OrderType
from pyqqq.utils.market_schedule import is_trading_time
import asyncio
import datetime as dtm


class SimpleBreakoutStrategy(BaseStrategy):
    """5분봉 돌파 매매 전략"""

    def __init__(self, environment, symbol="168360"):
        super().__init__(environment)
        self.symbol = symbol

    async def run(self):
        while self.clock.is_alive():
            try:
                if is_trading_time(self.clock.now()):
                    self.on_trade()

                await self.clock.sleep(60)

            except Exception as e:
                self.logger.exception(f"Error in strategy: {e}")
                await self.clock.sleep(60)

    def on_trade(self):
        positions = self.broker.get_positions()
        current_price = self.broker.get_price(self.symbol)
        # 현재 포지션 확인
        position = next((p for p in positions if p.asset_code == self.symbol), None)

        df = self.broker.get_minute_price(self.symbol)
        if len(df) < 5:
            return

        df["ma5"] = df["close"].rolling(5).mean()
        ohlcv = df.iloc[-1]

        ma5 = df["ma5"].iloc[-1]
        open = ohlcv["open"]
        close = ohlcv["close"]

        if not position and close > ma5 and ma5 > open:
            # 매수 신호
            quantity = int(10_000_000 / current_price)  # 1천만원어치
            self.broker.create_order(self.symbol, OrderSide.BUY, quantity, OrderType.MARKET)
            self.logger.info(f"매수 주문: {quantity}주 @ {current_price}")

        elif position and position.current_pnl > 3:
            # 매도 신호
            for position in positions:
                if position.asset_code == self.symbol:
                    self.broker.create_order(self.symbol, OrderSide.SELL, position.quantity, OrderType.MARKET)
                    self.logger.info(f"매도 주문: {position.quantity}주 @ {current_price}")

1.2 백테스팅 실행#

# backtest.py
import asyncio
import datetime as dtm
from pyqqq.backtest.environment import BacktestEnvironment
from strategy import SimpleBreakoutStrategy

async def run_backtest():
    # 백테스팅 환경 설정
    env = BacktestEnvironment(
        start_time=dtm.datetime(2024, 10, 2, 9, 0),
        end_time=dtm.datetime(2024, 10, 22, 15, 30),
        time_unit="minutes",
    )

    # 초기 자본금 설정
    env.get_broker().set_initial_cash(100_000_000)  # 1억원

    # 전략 실행
    strategy = SimpleBreakoutStrategy(env)
    await strategy.run()

    # 결과 분석
    broker = env.get_broker()

    # 1. 전체 거래 이력 분석
    report = broker.show_trading_history_report(make_file=True)
    print(f"- 총 거래횟수: {report['count']}")
    print(f"- 총 수익률: {report['total_pnl']:.2f}%")
    print(f"- 평균 수익률: {report['avg_pnl']:.2f}%")
    print(f"- ROI: {report['roi']:.2f}")

    # 2. 최종 포지션 확인
    broker.show_positions()


if __name__ == "__main__":
    asyncio.run(run_backtest())

1.3 백테스트 결과 분석#

백테스트 결과를 바탕으로 다음 사항들을 검토합니다:

  1. 수익성

    • 총 수익률과 ROI 확인

    • 개별 거래의 평균 수익률 검토

    • 최대 손실폭과 수익폭 확인

  2. 리스크 관리

    • 최대 손실 시점 분석

    • 연속 손실 구간 확인

    • 포지션 크기의 적절성 검토

  3. 전략 개선점 도출

    • 매매 타이밍 최적화

    • 손절/익절 전략 추가 검토

    • 포지션 사이징 조정

Step 2: 실전 거래 준비 및 실행#

2.1 계좌 설정#

  1. 한국투자증권 계좌 및 API 키 발급

  2. 환경변수 설정:

# 실전 계좌
export KIS_APP_KEY="your-app-key"
export KIS_APP_SECRET="your-app-secret"
export KIS_CANO="your-account-number"
export KIS_ACNT_PRDT_CD="your-product-code"

# 모의 계좌 (선택)
export PAPER_KIS_APP_KEY="your-paper-app-key"
export PAPER_KIS_APP_SECRET="your-paper-app-secret"
export PAPER_KIS_CANO="your-paper-account-number"
export PAPER_KIS_ACNT_PRDT_CD="your-paper-product-code"

2.2 모의투자 테스트#

백테스트에서 검증된 전략을 모의투자로 먼저 테스트합니다.

# paper_trading.py
import asyncio
from pyqqq.backtest.environment import KISDomesticEnvironment
from strategy import SimpleBreakoutStrategy

async def run_paper_trading():
    # 모의투자 환경 설정
    env = KISDomesticEnvironment(paper_trading=True)

    # 전략 실행
    strategy = SimpleBreakoutStrategy(env)
    await strategy.run()

if __name__ == "__main__":
    asyncio.run(run_paper_trading())

2.3 실전 거래 실행#

모의투자에서 충분히 검증된 후 실전 거래를 시작합니다.

# live_trading.py
import asyncio
from pyqqq.backtest.environment import KISDomesticEnvironment
from strategy import SimpleBreakoutStrategy

async def run():
    # 실전 환경 설정
    env = KISDomesticEnvironment(paper_trading=False)

    # 전략 실행
    strategy = SimpleBreakoutStrategy(env)
    await strategy.run()

if __name__ == "__main__":
    asyncio.run(run())