이어 가기
- Company
engines.company - Gather
engines.gather - 운전자본 quality (CCC + 회전율 추세 — L1 raw)
engines.recipe.workingCapitalQuality - Altman-lite 부도 위험 회피 필터 (역방향 블랙리스트)
engines.recipe.distressFilter - 부도 위험 2 모델 합의 (Altman Z″ + Ohlson O — L1 raw)
engines.recipe.creditDistressDual - Quality + Value 횡단 스크리닝 (Novy-Marx GP/A 기반)
engines.recipe.qualityValueScreen
절차
실행 순서
- 1
ACC = (NI − CFO) / Average Total Assets
- 2
발생액 (accruals) 이 큰 회사 → 미래 abnormal return 음(−). 1962-1991 백테스트 연 10%p 차이.
- 3
핵심 — 회계상 이익 ≠ 현금 이익. 차이가 클수록 미래 회귀 가능성.
- 4
M = −4.84 + 0.92·DSRI + 0.528·GMI + 0.404·AQI + 0.892·SGI + 0.115·DEPI − 0.172·SGAI − 0.327·LVGI + 4.679·TATA
- 5
8 변수 logit. M > −1.78 = 분식 의심. 검증 76% 적중.
- 6
Enron (1998 M-Score = +5.5) 분식 1 년 전 detection.
- 7
GP/A = (Sales − COGS) / Total Assets
- 8
ROE·ROA 보다 강력 quality factor. 가치 (B/M) 와 음(-) 상관.
- 9
`year : str`
- 10
`sloanAccruals : float` — (NI−CFO)/AvgAssets
- 11
`sloanFlag : bool` — > 0.10 = 분식 의심
- 12
`beneishM : float` — M-Score
학술 근거
3 학술 framework 동시 적용 — 각 모델이 잡지 못하는 신호를 다른 모델이 보완.
1. Sloan Accruals (1996)
Richard Sloan, “Do Stock Prices Fully Reflect Information in Accruals and Cash Flows about Future Earnings?” (The Accounting Review):
- ACC = (NI − CFO) / Average Total Assets
- 발생액 (accruals) 이 큰 회사 → 미래 abnormal return 음(−). 1962-1991 백테스트 연 10%p 차이.
- 핵심 — 회계상 이익 ≠ 현금 이익. 차이가 클수록 미래 회귀 가능성.
2. Beneish M-Score (1999)
Daniel Beneish, “The Detection of Earnings Manipulation” (Financial Analysts Journal):
- M = −4.84 + 0.92·DSRI + 0.528·GMI + 0.404·AQI + 0.892·SGI + 0.115·DEPI − 0.172·SGAI − 0.327·LVGI + 4.679·TATA
- 8 변수 logit. M > −1.78 = 분식 의심. 검증 76% 적중.
- Enron (1998 M-Score = +5.5) 분식 1 년 전 detection.
3. Novy-Marx GP/A (2013)
Robert Novy-Marx, “The Other Side of Value: The Gross Profitability Premium” (JFE):
- GP/A = (Sales − COGS) / Total Assets
- ROE·ROA 보다 강력 quality factor. 가치 (B/M) 와 음(-) 상관.
L1 데이터 직접 계산
3 모델 모두 BS + IS + CF 의 raw snakeId 에서 직접 계산. analysis 의 이익품질 axis 결과를 의존하지 않고 학술 공식 그대로 적용.
공개 호출 방식
import dartlab
import polars as pl
c = dartlab.Company("005930")
bs_df = c.show("BS", freq="Y")
is_df = c.show("IS", freq="Y")
cf_df = c.show("CF", freq="Y")
years = ["2025", "2024", "2023", "2022", "2021"]
def fetchSeries(df: pl.DataFrame, snake: str, years: list[str]) -> list[float]:
row = df.filter(pl.col("snakeId") == snake).select(years)
return row.to_numpy()[0].tolist() if row.height > 0 else [0.0] * len(years)
# 공통 raw
sales = fetchSeries(is_df, "sales", years)
cogs = fetchSeries(is_df, "cost_of_sales", years)
ni = fetchSeries(is_df, "net_income", years)
sga = fetchSeries(is_df, "selling_general_admin_expenses", years)
dep = fetchSeries(is_df, "depreciation_expense", years)
cfo = fetchSeries(cf_df, "cash_flow_from_operations", years)
assets = fetchSeries(bs_df, "total_assets", years)
ar = fetchSeries(bs_df, "trade_receivables", years)
ppe = fetchSeries(bs_df, "property_plant_equipment", years)
currAssets = fetchSeries(bs_df, "current_assets", years)
liab = fetchSeries(bs_df, "total_liabilities", years)
ltd = fetchSeries(bs_df, "long_term_debt", years)
# 1) Sloan Accruals (당년)
avgAssets = [(assets[i] + assets[i+1]) / 2 for i in range(len(years)-1)]
sloan = [(ni[i] - cfo[i]) / avgAssets[i] for i in range(len(years)-1)]
# 2) Beneish M-Score (당년 vs 전년 비교)
def beneishMScore(i: int) -> float:
dsri = (ar[i] / sales[i]) / (ar[i+1] / sales[i+1])
gmi = ((sales[i+1] - cogs[i+1]) / sales[i+1]) / ((sales[i] - cogs[i]) / sales[i])
aqi_curr = 1 - (currAssets[i] + ppe[i]) / assets[i]
aqi_prev = 1 - (currAssets[i+1] + ppe[i+1]) / assets[i+1]
aqi = aqi_curr / aqi_prev
sgi = sales[i] / sales[i+1]
depi = (dep[i+1] / (dep[i+1] + ppe[i+1])) / (dep[i] / (dep[i] + ppe[i]))
sgai = (sga[i] / sales[i]) / (sga[i+1] / sales[i+1])
lvgi = ((ltd[i] + (liab[i] - ltd[i])) / assets[i]) / ((ltd[i+1] + (liab[i+1] - ltd[i+1])) / assets[i+1])
tata = (ni[i] - cfo[i]) / assets[i]
return (-4.84 + 0.92*dsri + 0.528*gmi + 0.404*aqi + 0.892*sgi
+ 0.115*depi - 0.172*sgai - 0.327*lvgi + 4.679*tata)
beneishScores = [beneishMScore(i) for i in range(len(years)-1)]
# 3) Novy-Marx GP/A
gpa = [(sales[i] - cogs[i]) / assets[i] for i in range(len(years))]
triad = pl.DataFrame({
"year": years[:-1],
"sloanAccruals": sloan,
"sloanFlag": [s > 0.10 for s in sloan], # 10% 이상 = 위험
"beneishM": beneishScores,
"beneishFlag": [m > -1.78 for m in beneishScores],
"gpa": gpa[:-1],
"gpaPct": [g * 100 for g in gpa[:-1]],
}).with_columns(
(pl.col("sloanFlag").cast(pl.Int8) + pl.col("beneishFlag").cast(pl.Int8)).alias("riskScore")
) 호출 동작
c.show("BS" | "IS" | "CF", freq="Y")3 회 — 5 년 wide.- snakeId 로 12 raw 항목 추출.
- Sloan ACC = (NI − CFO) / AvgAssets — 5 점 시계열.
- Beneish M-Score = 8 변수 logit (당년/전년 비교) — 5 점 중 4 점 시계열 (전년 비교 위해 1 점 손실).
- Novy-Marx GP/A = (Sales − COGS) / Total Assets — 5 점 시계열.
- 종합 위험 점수 — Sloan flag + Beneish flag (0 ~ 2).
대표 반환 형태
triad : pl.DataFrame — 컬럼:
year : strsloanAccruals : float— (NI−CFO)/AvgAssetssloanFlag : bool— > 0.10 = 분식 의심beneishM : float— M-ScorebeneishFlag : bool— > −1.78 = 분식 의심gpa : float— Gross Profit / AssetsgpaPct : float— % 표기riskScore : int— 0 (안전), 1 (한 모델 의심), 2 (양 모델 의심)
한계
- Sloan 임계 0.10 — 일반적 임계. 산업별 분포 다름 (제조업 < 서비스업).
- Beneish 8 변수 모두 가용 —
selling_general_admin_expenses,depreciation_expense,property_plant_equipment등 snakeId 일부 회사 결손 가능. fallback 으로 0 처리 시 점수 왜곡. - Novy-Marx GP/A —
cost_of_sales분리 안 된 회사 (서비스업) 부적합. 매출원가 = 영업비용 가정 시 GP 과소. - 3 모델 모두 미국 시장 학술 검증 — 한국 시장 직접 검증 부재. 한국 chaebol 회계 특이성으로 false positive 가능.
- 분식 예측 아닌 의심 신호 — 점수 통과해도 실제 분식 보장 X.
한국 / 미국 시장 차이
- 한국: chaebol 계열사 internal trading 으로 매출/매출채권 변동 noise 큼 → DSRI · SGI 신뢰성 낮음. 별도재무 vs 연결재무 차이 큼.
- 미국: 본 framework 의 본 시장. 8 변수 모두 신뢰성 높음. SOX (2002) 이후 Beneish 검출률 다소 낮아짐.
연계 절차
- 본 recipe → 5 년 3 모델 점수 + riskScore.
- riskScore = 2 (양 모델 의심) →
engines.recipe.workingCapitalQuality의 AR/Inv vs Sales gap 점검. - 실제 분식 의심 회사 → 공시 (
c.disclosure(...)) 의 감사보고서 의견·정정공시 빈도 검증. - GP/A 강 (상위 10%) + Sloan 약 (하위 30%) = 진짜 quality.
engines.recipe.qualityValueScreen와 상호 검증. engines.recipe.creditDistressDual와 결합 — 분식 의심 + 부도 위험 = 강한 회피 신호.
기본 검증
- 3 모델 점수 시계열 확인 — 단년도 점수 신뢰 X, 5 년 추세 봄.
- Sloan + Beneish 모두 의심 시 — 실제 분식 case study (대우조선·한진중공업 등) 와 비교.
- GP/A 변화 추세 — 안정 또는 상승 = quality 우월. 급락 = 산업 사이클 또는 경쟁 심화.
- “M-Score = +0.5 = 분식 확정” 단정 X — 의심 신호이지 결정 X.
- 학술 모델은 통계적 임. 단일 회사 단정 위험. 점수 + 정성 (감사 보고서·정정공시·CFO 교체 빈도) 결합.
런타임
실행 환경별 호환성
| 환경 | 상태 | 비고 / 제한 |
|---|---|---|
| Local Python | supported | — |
| Server | supported | — |
| MCP | unknown | — |
| Web AI | unknown | — |
| Pyodide | limited |
|