recipes.quant.sizeFactor Recipes Recipe tested

Size 팩터 (Fama-French SMB) — log market cap percentile

Fama-French 1992 의 Size 팩터 — log(market cap) 의 peer 단면 percentile rank. small-cap premium 측면. 단순 시총이 아닌 log 변환 후 cross-section. 트리거 — 'Size 팩터 (Fama-French SMB)', 'size factor', 'sizeFactor'.

이 스킬

Size 팩터 (Fama-French SMB) — log market cap percentile

Fama-French 1992 의 Size 팩터 — log(market cap) 의 peer 단면 percentile rank. small-cap premium 측면. 단순 시총이 아닌 log 변환 후 cross-section. 트리거 — 'Size 팩터 (Fama-French SMB)', 'size factor', 'sizeFactor'.

Recipes tested recipes.quant.sizeFactor

연계 절차

이 절차의 단계

  1. 1

    small + value 결합 (Fama-French double sort).

  2. 2

    small × quality (junk 제거 후).

  3. 3
    recipes.meta.screen.smallCapDiscovery recipes.meta.screen.smallCapDiscovery

    small cap 발굴 트랙.

절차

실행 순서

  1. 1

    target market_cap (`dartlab.scan("valuation")` 의 시가총액 컬럼)

  2. 2

    `Company.industry()["peers"]` 상위 20 종목 × market_cap

  3. 3

    logCap = ln(marketCap) 변환 후 peer 단면

  4. 4

    percentile rank: percentileLargeToSmall = below count / N

  5. 5

    peer < 10 → percentile 불안정.

  6. 6

    산업 peer 단면이라 KOSPI 전체 단면 결과 다름 (병행 트랙 필요).

  7. 7

    small cap = high return 단정 금지 — 2010s anomaly 약화.

  8. 8

    IT 산업 시총 분포 right-skewed → log 변환 후에도 outlier 영향.

  9. 9

    smbRank > 0.7 (small) → `recipes.quant.qualityFactor` 로 small × quality (junk 제거).

  10. 10

    smbRank > 0.7 + value factor 동조 → `recipes.quant.valueFactor` 로 double sort.

  11. 11

    smbRank > 0.7 → `recipes.meta.screen.smallCapDiscovery` 로 small cap 발굴 트랙.

  12. 12

    peer < 10 이면 percentile 불안정.

예시

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

  • 005930 시총 rank — small / mid / large 어디
  • Fama-French size 팩터 — small-cap premium 상위 종목
  • log market cap cross-section 분위

출력

기대 결과

  • log(market cap) 단일값 + peer percentile
  • quartile 라벨 (small / mid / mid-large / large)
  • universe quartile 경계값 + 자기 종목 위치

공개 호출 방식

import dartlab
import polars as pl
import math

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

# 시가총액은 전종목 밸류에이션 스캔에서 한 번에 받는다 (종목마다 Company 를 여는 것보다 싸다).
valuation = dartlab.scan("valuation")   # 종목코드 · 종목명 · 시가총액 · PER · PBR · PSR · 배당수익률

def market_cap(code):
    row = valuation.filter(pl.col("종목코드") == code)
    if row.height == 0:
        return None
    return float(row["시가총액"][0] or 0)

own_cap = market_cap(target)
own_log_cap = math.log(own_cap) if own_cap and own_cap > 0 else None

try:
    peers = c.industry()["peers"][:20]
except Exception:
    peers = []

peer_rows = []
for p in peers:
    code = p.get("code") or p.get("stockCode")
    if not code or code == target:
        continue
    cap = market_cap(code)
    if cap and cap > 0:
        peer_rows.append({"code": code, "marketCap": cap, "logCap": math.log(cap)})

def rank(my_log_cap):
    vals = sorted([r["logCap"] for r in peer_rows])
    if not vals or my_log_cap is None:
        return None
    below = sum(1 for v in vals if v < my_log_cap)
    return below / len(vals)

# small size premium: 작을수록 rank 1
own_rank = rank(own_log_cap)
own_size_rank = (1 - own_rank) if own_rank is not None else None

table = pl.DataFrame([{
    "ownMarketCap": own_cap,
    "ownLogCap": own_log_cap,
    "peerCount": len(peer_rows),
    "ownPercentileLargeToSmall": own_rank,
    "smbRankSmallIs1": own_size_rank,
}])

emit_result(
    table=table,
    values={"marketCap": own_cap, "smbRank": own_size_rank, "peerCount": len(peer_rows)},
    date=None,
    sources=["dartlab://scan/valuation", "dartlab://industry/peers"],
)

호출 동작

1. 결론 도출

logMarketCap + percentile rank 단정. 예: “005930 시총 460조 (logCap=33.5) peer 20개 중 percentile=0.95 (대비 95%가 작음) → smbRankSmallIs1=0.05 → large-cap quartile (SMB premium 측면에서 unfavored). 반도체 industry peer 평균 시총 12조 — 005930 은 industry leader (size dominance).”

2. 핵심 근거 수집

  • target market_cap (dartlab.scan("valuation") 의 시가총액 컬럼)
  • Company.industry()["peers"] 상위 20 종목 × market_cap
  • logCap = ln(marketCap) 변환 후 peer 단면
  • percentile rank: percentileLargeToSmall = below count / N

3. 메커니즘 분석

target market_cap → log 변환
   logCap = ln(marketCap)
   원래 시총 분포가 power-law 라 log 변환 필요 (Fama-French 1992)

peer 단면 percentile:
   peer 20 종목 logCap 정렬
   own_rank = (target 보다 작은 peer 수) / N

SMB invert 표기 (small premium 측면):
   smbRankSmallIs1 = 1 - own_rank
   → 0.05 = 가장 큰 quartile (large-cap unfavored)
   → 0.95 = 가장 작은 quartile (small-cap favored)

quartile 분류:
   smbRank 0.75-1.0 → small (premium 후보)
   smbRank 0.5-0.75 → mid
   smbRank 0.25-0.5 → mid-large
   smbRank 0.0-0.25 → large (premium 미해당)

Fama-French SMB premium — 1926-2000 US 시장에서 small minus big = +3.5%/y annual. KR 시장에서도 일부 확인 (1990-2010 강함, 2010s 약화). 산업 peer 단면이라 산업 평균 size 따라 결론 변동.

4. 반례·한계

  • peer < 10 → percentile 불안정.
  • 산업 peer 단면이라 KOSPI 전체 단면 결과 다름 (병행 트랙 필요).
  • small cap = high return 단정 금지 — 2010s anomaly 약화.
  • IT 산업 시총 분포 right-skewed → log 변환 후에도 outlier 영향.

5. 후속 모니터링

  • smbRank > 0.7 (small) → recipes.quant.qualityFactor 로 small × quality (junk 제거).
  • smbRank > 0.7 + value factor 동조 → recipes.quant.valueFactor 로 double sort.
  • smbRank > 0.7 → recipes.meta.screen.smallCapDiscovery 로 small cap 발굴 트랙.

대표 반환 형태

column의미
ownMarketCap본 회사 시총
ownLogCaplog(시총)
ownPercentileLargeToSmallpeer 단면 (1 = 가장 큰)
smbRankSmallIs1invert (1 = 가장 작은)

연계 절차

  1. recipes.quant.valueFactor - small + value 결합 (Fama-French double sort).
  2. recipes.quant.qualityFactor - small × quality (junk 제거 후).
  3. recipes.meta.screen.smallCapDiscovery - small cap 발굴 트랙.

기본 검증

  • peer < 10 이면 percentile 불안정.
  • 산업 peer 단면이라 산업 평균 size 따라 결론 변동 — KOSPI 전체 단면 별 트랙으로 추가 가능.
  • small cap = high return 단정 금지 — anomaly 약화 시기 (2010s) 다수.

런타임

실행 환경별 호환성

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