전략 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 Nonecandle 객체
candle은 현재 캔들의 모든 정보를 담은 딕셔너리입니다.candle["history"]를 통해 이전 최대 100개 캔들에 접근할 수 있습니다.
| Key | Type | Description |
|---|---|---|
| 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는 현재 보유 자산 현황을 나타냅니다. 매 캔들 실행 직전의 최신 상태가 반영됩니다.
| Key | Type | Description |
|---|---|---|
| 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)
| Key | Type | Description |
|---|---|---|
| "action" | str | "buy" 고정값 |
| "amount" | float | 현금 중 몇 분율(0.0~1.0)을 매수할지. 0.95 = 현금의 95% 매수. 수수료 고려 시 1.0보다 작게 권장. |
return {"action": "buy", "amount": 0.95} # 현금의 95%로 매수매도 (sell)
| Key | Type | Description |
|---|---|---|
| "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 문서앱으로 돌아가기 →