recipes.technical.rsiBollingerCluster Recipes Recipe curated

RSI + 볼린저 밴드 동시 cluster (이중 oversold/overbought)

RSI(14) < 30 또는 > 70 + 동시 가격이 볼린저 밴드 (20, 2σ) 하단/상단 close 인 row 만 cluster 로 표기. 단일 oscillator 함정 회피 (두 정의 동시 충족만 신호). 트리거 — 'RSI + 볼린저 밴드 동시 cluster (이중 oversold/overbought)', 'rsi bollinger cluster', 'rsiBollingerCluster'.

이 스킬

RSI + 볼린저 밴드 동시 cluster (이중 oversold/overbought)

RSI(14) < 30 또는 > 70 + 동시 가격이 볼린저 밴드 (20, 2σ) 하단/상단 close 인 row 만 cluster 로 표기. 단일 oscillator 함정 회피 (두 정의 동시 충족만 신호). 트리거 — 'RSI + 볼린저 밴드 동시 cluster (이중 oversold/overbought)', 'rsi bollinger cluster', 'rsiBollingerCluster'.

Recipes curated recipes.technical.rsiBollingerCluster

연계 절차

이 절차의 단계

  1. 1
    recipes.technical.momentumFlowDivergence recipes.technical.momentumFlowDivergence

    cluster 시점 수급 정합.

  2. 2

    cluster 시점 imbalance.

절차

실행 순서

  1. 1

    가격 row 60+ 거래일 (Company.gather('price') high/low/close)

  2. 2

    RSI(14) — 14일 평균 상승 / (상승+하락) × 100

  3. 3

    볼린저 밴드(20, 2σ) — 20일 이동평균 ± 2 × 20일 표준편차

  4. 4

    강한 추세 (uptrend) 중에는 RSI > 70 + close > upper 가 정상 — overbought 신호 false positive.

  5. 5

    변동성 낮은 종목 (zero band width) 의 볼린저 cluster 신뢰도 낮음.

  6. 6

    60일 미만 history 시 cluster 측정 불가.

  7. 7

    단일 cluster 만으로 매수/매도 단정 금지 — 다른 신호 (수급 · 가격 모멘텀) cross-check.

  8. 8

    oversoldCluster 발생 직후: `recipes.technical.priceVolumeZScore` 로 거래량 동행 확인 (오버솔드 + 거래량 폭증 = 저점 매수 후보 강함).

  9. 9

    overboughtCluster 지속 (3 row+): `recipes.technical.atrRegimeShift` 로 변동성 확대 확인 (고점 신호).

  10. 10

    신호 없는 종목 (cluster 0): 60d 추세 우세 — `recipes.technical.movingAverageConfluence` 로 추세 합의 확인.

  11. 11

    거래일 < 30 이면 결론 X.

  12. 12

    갭상승·하한가 등 단일 day noise 는 cluster 처리 X.

예시

이런 질문이 들어오면 이 skill 을 쓴다

  • 005930 RSI + 볼린저 밴드 동시 oversold 신호
  • 이중 과매수 cluster 형성된 날
  • RSI < 30 + 볼린저 하단 동시 close

출력

기대 결과

  • RSI(14) 현재값 + 볼린저 상단 / 하단 close 여부
  • cluster row list (oversold cluster + overbought cluster 분리)
  • 라벨 (oversold cluster / overbought cluster / 무신호)

공개 호출 방식

import dartlab
import polars as pl
import statistics

target = "005930"

try:
    px = dartlab.gather("price", target).head(80).to_dicts()
except Exception:
    px = []
px.sort(key=lambda r: str(r.get("date") or r.get("tradeDate")))
closes = [(r.get("date") or r.get("tradeDate"), float(r.get("close") or 0)) for r in px if r.get("close")]

if len(closes) < 30:
    table = pl.DataFrame(schema={"date": pl.Utf8, "close": pl.Float64, "rsi": pl.Float64,
                                  "bbUpper": pl.Float64, "bbLower": pl.Float64, "signal": pl.Utf8})
else:
    # RSI(14)
    rsi_series = [None]*14
    gains, losses = [], []
    for i in range(1, len(closes)):
        diff = closes[i][1] - closes[i-1][1]
        gains.append(max(diff, 0))
        losses.append(max(-diff, 0))
        if i >= 14:
            avg_gain = statistics.mean(gains[-14:])
            avg_loss = statistics.mean(losses[-14:])
            rs = avg_gain / avg_loss if avg_loss else 0
            rsi = 100 - (100 / (1 + rs)) if avg_loss else 100
            rsi_series.append(rsi)
    # Bollinger(20, 2)
    bb_upper = [None]*20
    bb_lower = [None]*20
    for i in range(20, len(closes)):
        window = [c[1] for c in closes[i-20:i]]
        mu = statistics.mean(window)
        sd = statistics.stdev(window)
        bb_upper.append(mu + 2*sd)
        bb_lower.append(mu - 2*sd)
    rows = []
    for i, (d, c_val) in enumerate(closes):
        if i < 20 or rsi_series[i] is None: continue
        rsi = rsi_series[i]
        bu = bb_upper[i]
        bl = bb_lower[i]
        signal = "oversoldCluster" if (rsi < 30 and c_val < bl) else 
                 "overboughtCluster" if (rsi > 70 and c_val > bu) else "normal"
        if signal != "normal":
            rows.append({"date": d, "close": c_val, "rsi": rsi, "bbUpper": bu, "bbLower": bl, "signal": signal})
    table = pl.DataFrame(rows) if rows else pl.DataFrame(
        schema={"date": pl.Utf8, "close": pl.Float64, "rsi": pl.Float64,
                "bbUpper": pl.Float64, "bbLower": pl.Float64, "signal": pl.Utf8})

emit_result(
    table=table,
    values={"clusterCount": table.height},
    date=str(closes[-1][0]) if closes else None,
    sources=["dartlab://gather/price"],
)

호출 동작

1. 결론 도출

RSI + 볼린저 동시 cluster 단정 (oversoldCluster / overboughtCluster / 무신호). 예: “최근 60 거래일 oversold cluster 3 건 (RSI<30 + close<lower), overbought 0 건 — 이중 oversold 신호 활성.”

2. 핵심 근거 수집

  • 가격 row 60+ 거래일 (Company.gather(‘price’) high/low/close)
  • RSI(14) — 14일 평균 상승 / (상승+하락) × 100
  • 볼린저 밴드(20, 2σ) — 20일 이동평균 ± 2 × 20일 표준편차

3. 메커니즘 분석

60거래일 close → RSI(14) 시계열 + 볼린저(20, 2σ) upper/lower band

각 row 에서 두 조건 동시 점검
   RSI<30 AND close<lower → oversoldCluster (이중 oversold)
   RSI>70 AND close>upper → overboughtCluster (이중 overbought)
   둘 다 아님              → 무신호

단일 oscillator 함정 회피 — RSI 또는 볼린저 단독으로는 흔한 false positive. 두 정의 동시 충족만 신호로 사용. cluster row 수 ↑ 또는 연속 발생 = 신호 강도 ↑.

4. 반례·한계

  • 강한 추세 (uptrend) 중에는 RSI > 70 + close > upper 가 정상 — overbought 신호 false positive.
  • 변동성 낮은 종목 (zero band width) 의 볼린저 cluster 신뢰도 낮음.
  • 60일 미만 history 시 cluster 측정 불가.
  • 단일 cluster 만으로 매수/매도 단정 금지 — 다른 신호 (수급 · 가격 모멘텀) cross-check.

5. 후속 모니터링

  • oversoldCluster 발생 직후: recipes.technical.priceVolumeZScore 로 거래량 동행 확인 (오버솔드 + 거래량 폭증 = 저점 매수 후보 강함).
  • overboughtCluster 지속 (3 row+): recipes.technical.atrRegimeShift 로 변동성 확대 확인 (고점 신호).
  • 신호 없는 종목 (cluster 0): 60d 추세 우세 — recipes.technical.movingAverageConfluence 로 추세 합의 확인.

대표 반환 형태

column의미
date거래일
close종가
rsiRSI(14)
bbUpper / bbLower볼린저 밴드
signaloversoldCluster / overboughtCluster

연계 절차

  1. recipes.technical.momentumFlowDivergence - cluster 시점 수급 정합.
  2. recipes.sentiment.flowImbalance - cluster 시점 imbalance.

기본 검증

  • 거래일 < 30 이면 결론 X.
  • 갭상승·하한가 등 단일 day noise 는 cluster 처리 X.
  • 신호 단독 매매 결정 X — 펀더멘털 결합 후.

런타임

실행 환경별 호환성

환경상태비고 / 제한
Local Python supported·
Server supported·
MCP supported·
Web AI limited·
Pyodide limited·