recipes.sentiment.shortBalanceMomentum Recipes Recipe deprecated

공매도 잔고 변화율 모멘텀

일별 공매도 잔고 (short balance, 또는 그 변화율) 의 20 거래일 z-score. 잔고 급증은 약세 베팅 cluster, 급감은 short covering 단서. 추론 라벨 없이 정량 모멘텀만. gather L1 단일. 트리거 — '공매도 잔고', 'short balance', 'short covering'.

이 스킬

공매도 잔고 변화율 모멘텀

일별 공매도 잔고 (short balance, 또는 그 변화율) 의 20 거래일 z-score. 잔고 급증은 약세 베팅 cluster, 급감은 short covering 단서. 추론 라벨 없이 정량 모멘텀만. gather L1 단일. 트리거 — '공매도 잔고', 'short balance', 'short covering'.

Recipes deprecated recipes.sentiment.shortBalanceMomentum

연계 절차

이 절차의 단계

  1. 1

    외국인/기관 수급과 같이 보면 *방향* 단서.

  2. 2
    실질지배력·연결범위·방어권 판단 진단 (IFRS 10) recipes.fundamental.quality.forensics.controllingPowerJudgment

    잔고 급증 시 의결권 분쟁 의심.

절차

실행 순서

  1. 1

    거래일 < 30 이면 z-score 결론 X.

  2. 2

    공매도 일변동량 (volume) 과 잔고 (stock) 혼동 금지.

  3. 3

    short covering 신호를 "상승 시그널" 로 라벨링 금지 — covering 은 *기존 약세 베팅 해제* 의 정량 사실일 뿐.

출력

기대 결과

  • 일별 공매도 잔고 + 변화율 표
  • 20일 rolling z-score
  • z ≥ 2 (잔고 급증) / ≤ -2 (covering) row 카운트

공개 호출 방식

import dartlab
import polars as pl
import statistics

target = "005930"
c = dartlab.Company(target)

def rows(value, limit=120):
    if hasattr(value, "head") and hasattr(value, "to_dicts"):
        return value.head(limit).to_dicts()
    if isinstance(value, list):
        return value[:limit]
    return []

short_rows = rows(c.gather("shortBalance"), limit=120)

series = []
for r in short_rows:
    bal = r.get("shortBalance") or r.get("short_balance") or r.get("balanceShares")
    try:
        bal = float(bal) if bal is not None else None
    except (TypeError, ValueError):
        bal = None
    series.append({
        "date": r.get("date") or r.get("tradeDate"),
        "balance": bal,
    })

series.sort(key=lambda x: str(x["date"] or ""))
prev = None
for s in series:
    if prev is not None and s["balance"] is not None and prev > 0:
        s["changePct"] = (s["balance"] - prev) / prev
    else:
        s["changePct"] = None
    if s["balance"] is not None:
        prev = s["balance"]

WINDOW = 20
changes = [s["changePct"] for s in series if s["changePct"] is not None]
for idx, s in enumerate(series):
    if s["changePct"] is None:
        s["z"] = None
        continue
    earlier = [c for c in changes[:idx] if c is not None][-WINDOW:]
    if len(earlier) < 5:
        s["z"] = None
        continue
    mu = statistics.mean(earlier)
    sd = statistics.stdev(earlier) if len(earlier) > 1 else 0
    s["z"] = (s["changePct"] - mu) / sd if sd > 0 else None

table = pl.DataFrame(series) if series else pl.DataFrame(
    schema={"date": pl.Utf8, "balance": pl.Float64, "changePct": pl.Float64, "z": pl.Float64}
)

surge = int((table["z"].fill_null(0) >= 2).sum()) if table.height else 0
covering = int((table["z"].fill_null(0) <= -2).sum()) if table.height else 0

emit_result(
    table=table,
    values={"rows": table.height, "surgeCount": surge, "coveringCount": covering},
    date=str(table["date"].max()) if table.height else None,
    sources=["dartlab://gather/shortBalance"],
)

호출 동작

일별 공매도 잔고 시계열에서 직전일 대비 변화율 산출 후 20 거래일 rolling z-score. z ≥ 2 = 잔고 급증, z ≤ -2 = covering 단서. 잔고 absolute 크기 자체 는 결론 근거 아님.

대표 반환 형태

column의미
date거래일
balance공매도 잔고 (주)
changePct직전일 대비 변화율
z20 거래일 변화율 z-score

연계 절차

  1. recipes.sentiment.flowImbalance - 외국인/기관 수급과 같이 보면 방향 단서.
  2. recipes.fundamental.quality.forensics.controllingPowerJudgment - 잔고 급증 시 의결권 분쟁 의심.

기본 검증

  • 거래일 < 30 이면 z-score 결론 X.
  • 공매도 일변동량 (volume) 과 잔고 (stock) 혼동 금지.
  • short covering 신호를 “상승 시그널” 로 라벨링 금지 — covering 은 기존 약세 베팅 해제 의 정량 사실일 뿐.

런타임

실행 환경별 호환성

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

실패 회피

흔한 실패 · 절대 금지

흔한 실패
  • 잔고 (shares outstanding 분모 변동) 와 잔고비율 (잔고/총주식수) 혼동
  • 공매도 일변동량 (daily short volume) 과 잔고 (stock) 혼동
  • 거래일 < 30 으로 z-score 노이즈
절대 금지
  • 공매도 잔고만으로 약세 결론
  • short covering 신호를 "상승 시그널" 로 라벨링