← 이지큐/전략 API 문서

전략 API 문서

이지큐 백테스트 엔진은 여러분의 Python 전략을 받아 실제 시장 데이터로 검증합니다. 이 문서는 전략 인터페이스, 사용 가능한 데이터, 그리고 다양한 전략 예시를 제공합니다.

개요

전략은 on_candle 함수 또는 클래스를 통해 매 캔들마다 호출됩니다. 엔진은 OHLCV 데이터와 현재 포트폴리오 상태를 전달하며, 여러분의 코드는 매수/매도/홀드 중 하나를 반환합니다.

📊
데이터 접근
매 캔들의 OHLCV + 최근 100개 히스토리
💼
포트폴리오
현금, 보유 수량, 자산 현황 실시간 반영
신호 반환
buy / sell / None (홀드) 중 선택

인터페이스

(A) 함수형 — 상태가 불필요할 때

가장 단순한 형태. 매 캔들마다 독립적으로 호출됩니다. 캔들 간 상태를 저장하려면 클래스형을 사용하세요.

def on_candle(candle, portfolio):
    # candle  : dict — 현재 캔들 데이터 (OHLCV + history)
    # portfolio: dict — 현재 포트폴리오 상태

    # 매수 신호
    return {"action": "buy", "amount": 0.95}

    # 매도 신호
    return {"action": "sell", "amount": 1.0}

    # 홀드 (아무것도 하지 않음)
    return None

(B) 클래스형 — 캔들 간 상태 보존이 필요할 때

self를 통해 캔들 간에 변수를 유지할 수 있습니다. 누적 통계, 상태 머신, 이전 신호 저장 등에 활용됩니다.

class Strategy:
    def __init__(self):
        # 전략 시작 시 1회 호출
        # 유지할 상태 변수를 여기서 초기화
        self.prices        = []
        self.trade_count   = 0
        self.last_signal   = None

    def on_candle(self, candle, portfolio):
        # 매 캔들마다 호출됨
        self.prices.append(candle["close"])
        # ... 전략 로직 ...
        return None

candle 객체

candle은 현재 캔들의 모든 정보를 담은 딕셔너리입니다.candle["history"]를 통해 이전 최대 100개 캔들에 접근할 수 있습니다.

KeyTypeDescription
candle["open"]float현재 캔들의 시가 (opening price)
candle["high"]float현재 캔들의 고가 (highest price)
candle["low"]float현재 캔들의 저가 (lowest price)
candle["close"]float현재 캔들의 종가 (closing price)
candle["volume"]float현재 캔들의 거래량 (trading volume)
candle["history"]list[dict]이전 최대 100개 캔들. 각 항목은 open/high/low/close/volume 키를 가짐. history 키는 없음.
# history 사용 예시
prices = [h["close"] for h in candle["history"]]   # 이전 100개 종가
volumes = [h["volume"] for h in candle["history"]]  # 이전 100개 거래량

# 현재 캔들 포함
all_prices = [h["close"] for h in candle["history"]] + [candle["close"]]

# 최근 20개만 사용
recent_20 = [h["close"] for h in candle["history"][-20:]]

portfolio 객체

portfolio는 현재 보유 자산 현황을 나타냅니다. 매 캔들 실행 직전의 최신 상태가 반영됩니다.

KeyTypeDescription
portfolio["cash"]float현재 사용 가능한 현금 (미투자 자금)
portfolio["units"]float현재 보유 중인 자산 수량 (코인/주식 등)
portfolio["position_value"]float보유 포지션의 현재 평가액 (units × close)
portfolio["equity"]float총 자산 = cash + position_value
portfolio["initial_cash"]float전략 시작 시 설정한 초기 자본금
# 포지션 여부 확인
has_position = portfolio["position_value"] > 0

# 수익률 계산
pnl_pct = (portfolio["equity"] / portfolio["initial_cash"] - 1) * 100

# 사용 가능한 현금
available_cash = portfolio["cash"]

# 전량 보유 중인지 확인
fully_invested = portfolio["cash"] < 1.0

반환값

매수 (buy)

KeyTypeDescription
"action"str"buy" 고정값
"amount"float현금 중 몇 분율(0.0~1.0)을 매수할지. 0.95 = 현금의 95% 매수. 수수료 고려 시 1.0보다 작게 권장.
return {"action": "buy", "amount": 0.95}   # 현금의 95%로 매수

매도 (sell)

KeyTypeDescription
"action"str"sell" 고정값
"amount"float보유 포지션 중 몇 분율을 매도할지. 1.0 = 전량 매도. 0.5 = 절반 매도.
return {"action": "sell", "amount": 1.0}   # 전량 매도
return {"action": "sell", "amount": 0.5}   # 절반 매도

홀드 (hold)

아무 행동도 하지 않으려면 None을 반환하거나, 아무것도 반환하지 않으면 됩니다.

return None   # 아무것도 하지 않음 (홀드)

전략 예시

아래 전략들은 모두 앱의 에디터에서 바로 사용할 수 있습니다. 각 전략의 주석을 꼼꼼히 읽으면 핵심 로직을 이해하고 변형할 수 있습니다.

# ─────────────────────────────────────────────────────────────────────────
# SMA Crossover Strategy
# ─────────────────────────────────────────────────────────────────────────
# 원리: 단기 이동평균(SMA10)이 장기 이동평균(SMA30)을 상향 돌파하면 매수,
#        하향 돌파하면 매도하는 고전적 추세 추종 전략.
#
# 장점: 구현이 단순하고 직관적. 강한 추세 장세에서 효과적.
# 단점: 횡보장에서 잦은 매매(휩쏘)로 손실 발생 가능.
# ─────────────────────────────────────────────────────────────────────────

def on_candle(candle, portfolio):
    history = candle["history"]

    # 최소 30개 캔들이 필요 (장기 SMA 계산)
    if len(history) < 30:
        return None

    # 현재 종가를 포함한 가격 배열 생성
    prices = [h["close"] for h in history] + [candle["close"]]

    # 단기 SMA(10)와 장기 SMA(30) 계산
    sma_short = sum(prices[-10:]) / 10
    sma_long  = sum(prices[-30:]) / 30

    has_position = portfolio["position_value"] > 0

    # 매수 조건: 포지션 없고 단기 > 장기 (골든 크로스)
    if not has_position and sma_short > sma_long:
        return {"action": "buy", "amount": 0.95}   # 현금의 95% 매수

    # 매도 조건: 포지션 있고 단기 < 장기 (데드 크로스)
    if has_position and sma_short < sma_long:
        return {"action": "sell", "amount": 1.0}   # 전량 매도

    return None  # 홀드

팁 & 주의사항

📏히스토리 길이 확인
이동평균 N기간을 사용하려면 반드시 history 길이를 먼저 확인하세요.
history는 최대 100개까지 제공됩니다.

if len(history) < N:
    return None  # 데이터 부족
⚠️amount는 분율 (0.0~1.0)
amount는 비율이지 금액이 아닙니다.
amount=0.95는 "현금의 95%로 매수"를 의미합니다.
수수료를 고려해 1.0보다 약간 작게 설정하세요.

return {"action": "buy", "amount": 0.95}
🔒포지션 중복 매수 방지
이미 포지션이 있는 상태에서 또 매수하면
기존 포지션에 추가 매수됩니다.
일반적으로 포지션 여부를 먼저 확인하세요.

has_pos = portfolio["position_value"] > 0
if not has_pos and 매수조건:
    return {"action": "buy", "amount": 0.95}
🏗️클래스형 상태 초기화
클래스형 전략은 __init__에서 반드시 상태를 초기화하세요.
백테스트 시작 시 Strategy()가 1회 호출됩니다.

class Strategy:
    def __init__(self):
        self.peak   = None
        self.count  = 0
📉과최적화(Overfitting) 주의
특정 기간 데이터에만 맞게 파라미터를 조정하면
실제 시장에서 성과가 나빠질 수 있습니다.
다양한 기간에서 결과를 검증하세요.
🐛에러는 errors 탭에서 확인
전략 코드에서 예외가 발생해도 백테스트는 계속됩니다.
에러는 하단 패널의 errors 탭에서 확인하세요.
Python 표준 라이브러리(math, statistics 등)를 사용할 수 있습니다.
이지큐 전략 API 문서앱으로 돌아가기 →