콘텐츠로 이동

캔들패턴 전략

QuantiqDSL의 OHLC 데이터를 활용하여 캔들스틱 패턴을 감지하는 전략 예제입니다.


1. 망치형 (Hammer) 패턴

하락 추세에서 긴 아래 꼬리와 작은 몸통을 가진 양봉이 나타나면 반전 신호입니다.

version("1.0")
description("망치형(Hammer) 패턴 감지 전략")

c = chart("1D")
sma20 = ta.sma(c.close, 20)

# 캔들 구성 요소
body = abs(c.close[0] - c.open[0])
upper_wick = c.high[0] - max(c.close[0], c.open[0])
lower_wick = min(c.close[0], c.open[0]) - c.low[0]
full_range = c.high[0] - c.low[0]

# 망치형 조건
is_hammer = (
    full_range > 0 and
    lower_wick > body * 2 and       # 아래 꼬리가 몸통의 2배 이상
    upper_wick < body * 0.5 and     # 위 꼬리가 작음
    body > 0 and                     # 양봉
    c.close[0] > c.open[0]
)

# 하락 추세 확인
downtrend = c.close < sma20

c.line("SMA 20", sma20, color="blue")

if is_hammer and downtrend:
    c.marker("HAMMER", color="green", position="below", shape="triangle")
    buy(tag=f"망치형 패턴 — 하락추세 반전 기대")
else:
    hold()

2. 장악형 (Engulfing) 패턴

이전 봉을 완전히 감싸는 큰 봉이 나타나면 추세 전환 신호입니다.

version("1.0")
description("장악형(Engulfing) 패턴 전략")

c = chart("1D")

# 이전 봉
prev_open = c.open[1]
prev_close = c.close[1]
prev_body = abs(prev_close - prev_open)

# 현재 봉
curr_open = c.open[0]
curr_close = c.close[0]
curr_body = abs(curr_close - curr_open)

# 상승 장악형: 이전 음봉을 현재 양봉이 감쌈
bullish_engulf = (
    prev_close < prev_open and          # 이전: 음봉
    curr_close > curr_open and           # 현재: 양봉
    curr_open <= prev_close and          # 현재 시가 <= 이전 종가
    curr_close >= prev_open and          # 현재 종가 >= 이전 시가
    curr_body > prev_body                # 현재 몸통 > 이전 몸통
)

# 하락 장악형: 이전 양봉을 현재 음봉이 감쌈
bearish_engulf = (
    prev_close > prev_open and          # 이전: 양봉
    curr_close < curr_open and           # 현재: 음봉
    curr_open >= prev_close and          # 현재 시가 >= 이전 종가
    curr_close <= prev_open and          # 현재 종가 <= 이전 시가
    curr_body > prev_body                # 현재 몸통 > 이전 몸통
)

if bullish_engulf:
    c.marker("BUL ENG", color="green", position="below", shape="triangle")
    buy(tag="상승 장악형 패턴")
elif bearish_engulf:
    c.marker("BER ENG", color="red", position="above", shape="triangle")
    sell(tag="하락 장악형 패턴")
else:
    hold()

3. 도지 (Doji) 패턴

시가와 종가가 거의 같은 봉으로, 시장의 불확실성을 나타냅니다.

version("1.0")
description("도지(Doji) + 추세 반전 전략")
param("doji_threshold", "doji threshold", 0.1)

c = chart("1D")
rsi = ta.rsi(c.close, 14)

body = abs(c.close[0] - c.open[0])
full_range = c.high[0] - c.low[0]

# 도지 조건: 몸통이 전체 범위의 10% 이하
is_doji = full_range > 0 and (body / full_range) < script_params["doji_threshold"]

if is_doji:
    c.marker("DOJI", color="yellow", position="above", shape="circle")
    log(f"도지 감지 — 몸통 비율: {body/full_range*100:.1f}%")

    # 도지 + RSI 조합으로 방향 판단
    if rsi[0] < 30:
        buy(tag="도지 + RSI 과매도 → 반등 기대")
    elif rsi[0] > 70:
        sell(tag="도지 + RSI 과매수 → 반락 기대")
    else:
        hold(tag="도지 감지 — 방향 불확실")
else:
    hold()

4. 연속 양봉/음봉 전략

연속으로 같은 방향의 봉이 나타나면 추세가 강하다고 판단합니다.

version("1.0")
description("연속 양봉/음봉 전략")
param("count", "count", 3)

c = chart("1D")

# 연속 양봉 카운트
bullish_count = 0
for i in range(script_params["count"]):
    if c.close.is_valid(i) and c.close[i] > c.open[i]:
        bullish_count = bullish_count + 1
    else:
        break

# 연속 음봉 카운트
bearish_count = 0
for i in range(script_params["count"]):
    if c.close.is_valid(i) and c.close[i] < c.open[i]:
        bearish_count = bearish_count + 1
    else:
        break

target = script_params["count"]

if bullish_count >= target:
    buy(tag=f"{bullish_count}연속 양봉 — 강한 상승 모멘텀")
elif bearish_count >= target:
    sell(tag=f"{bearish_count}연속 음봉 — 강한 하락 모멘텀")
else:
    hold()

5. 갭 전략

갭업/갭다운 발생을 감지하여 매매합니다.

version("1.0")
description("갭(Gap) 전략")
param("gap_pct", "gap pct", 2.0)

c = chart("1D")

# 갭 계산 (현재 시가 vs 이전 종가)
if c.close.is_valid(1):
    gap = (c.open[0] - c.close[1]) / c.close[1] * 100

    if gap > script_params["gap_pct"]:
        c.marker("GAP UP", color="green", position="below", shape="arrow")
        log(f"갭업: {gap:.1f}%")

        # 갭업 후 양봉이면 추세 지속
        if c.close[0] > c.open[0]:
            buy(tag=f"갭업({gap:.1f}%) 후 양봉 — 상승 지속")
        else:
            hold(tag="갭업 후 음봉 — 되메움 가능성")

    elif gap < -script_params["gap_pct"]:
        c.marker("GAP DN", color="red", position="above", shape="arrow")
        log(f"갭다운: {gap:.1f}%")

        rsi = ta.rsi(c.close, 14)
        if rsi[0] < 30:
            buy(tag=f"갭다운({gap:.1f}%) + RSI 과매도 반등")
        else:
            sell(tag=f"갭다운({gap:.1f}%) — 하락 지속")
    else:
        hold()
else:
    hold()

관련 문서