2026-08-11 ~ 08-15. 구독 개시부터 전략 채택, 그 이후 검증까지의 기록이다.
이 문서의 목적은 같은 실험을 두 번 하지 않는 것이다. 성공한 하나보다 기각된 스무 개가 더 많은 지면을 차지하는 이유가 그것이다.
| 채택 전략 | 미국 소형주 8팩터(가치4+성장4) + 200일 이평 타이밍 |
| 성과 (OOS 18.6년, 보수 기준) | CAGR 16.90% · MDD −23.7% · Sharpe 0.756 · DSR 0.992 |
| 측정 프로토콜 | walk-forward · 학습 5년 · 24폴드 (§5.4). 2003~2007 을 더하면 CAGR 23.78% 이나 표제로 쓰지 않는다 |
| 설정 파일 | configs/strategy_quantus_timed.json |
| 유니버스 파일 | ~/data/universe_quantus.txt (6,895종목) |
| 스토어 | ~/data/us_micro.duckdb |
| 시험한 것 | 35가지 이상 · 정교한 기법 중 채택 전략을 이긴 것은 없다 |
| ⚠ 2026-08-16 검증 | 채택 전략은 방어 장치를 켜면 무너진다 (Sharpe 1.047 → −0.224, §5.5). 유니버스의 98% 가 사라지고 원금 상한은 1억 남짓이다 |
| 운용 후보 | E안 대형주 5팩터 + 200일 타이밍 — 방어 켠 채로 Sharpe 0.727 · DSR 0.996 · 용량 제약 없음 (§5.8) |
| 테이블 | 행수 | 종목 | 기간 |
|---|---|---|---|
prices |
45,663,897 | 20,931 | 1997-12-31 ~ 2026-08-14 |
fundamentals |
654,911 | — | 1992-12-31 ~ |
actions |
670,830 | 31,998 | 1997-12-31 ~ |
sp500 |
59,672 | 1,191 | 1990-02-01 ~ |
institutions |
669,593 | — | 13F 티커 집계 |
insiders |
312,911 | — | — |
tickers |
43,919 | — | — |
가격은 SEP(주가) + DAILY(시총·EV) 병합이며, 벤치마크 SPY 는 SFP(펀드)에서 별도 적재했다.
2026-08-15 전량 재구축 기준이다. 직전(08-12) 대비 prices 가 21,963종목·4,631만행에서
줄었는데 절단이 아니라 벤더가 SEP 에서 워런트를 뺀 것이다 — 사라진 1,037 중
1,035가 워런트 카테고리이고 2는 개명이다. 상세와 판정 방법은 04-data-contract.md §1.
institutions 는 CSV 리더 버그 수정으로 이번에 처음 채워졌다.
폐지 종목이 실제로 들어 있는지 직접 확인했다.
| 티커 | 회사 | 데이터 종료 |
|---|---|---|
ENRNQ |
엔론 | 2004-11-17 |
AAMRQ |
구 아메리칸항공 | 2013-12-05 |
ABKFQ |
암박 | 2010-11-16 |
actions 에 폐지 19,227건 · 분할 12,916건이 1997년부터 기록돼 있다.
| 항목 | 확인 내용 |
|---|---|
| 유료 플랜 폐지 종목 | 포함 (벤더 직접 회신, 2026-08-11) |
| 요청당 티커 상한 | 30개 — 초과 시 400 Too many tickers |
| 가격 축 | 데이터셋($19/$9/$9/Bundle $29) × 히스토리(5년 $30 / 10년 $49 / 풀 $69) |
| 풀 히스토리 | 펀더멘털 1998년~ |
| 벌크 다운로드 | API 키로 불가 — 브라우저 로그인 세션 필요 (signedIn:false) |
| SP500 이력 | current/historical/added/removed 전부 존재, 1957년~ |
| ACTIONS 어휘 | split·dividend·listed·delisted·spinoff 등 |
전부 “조용히 잘못되는” 유형이었다.
TruncatedDataError.datekey < reportperiod 가 42,841행당 2행. 넣으면 look-ahead, 전량 폐기는 과하다 → 제외하되 로그, 1% 초과 시 중단.isdelisted 미정규화 — CSV 경로만 리네임이 빠져 폐지 여부가 통째로 NULL.accessible_tickers() 자체가 생존편향 — 최근 4개 분기 재무로 유니버스를 찾으므로 폐지 종목이 원리적으로 안 잡힌다.deflated_sharpe_ratio 는 sr_var 를 기간(일별) 단위로 요구하는데,
넘기던 값은 목적함수가 만든 연율화 Sharpe 의 분산이었다.
252배 부풀려진 분산 → SR₀ 가 √252 ≈ 15.9배 → DSR 이 성과와 무관하게 0 에 붙는다.
같은 수익률·같은 시도 횟수에서 DSR 0.000 vs 0.910.
이 버그 때문에 초기 두 전략을 “우연과 구분 불가”로 기각했고, 그 판정은 무효였다. 목적함수가 Sharpe 가 아니면(Calmar 등) 분산의 의미가 달라지므로 추정하지 않고 보수적 기본값(1/T)으로 후퇴한다.
전수 검토(2026-08-15)에서 나온 어긋남이다. 설계 문서 00-overview.md §6·§7 은
세 항목을 필수로 못박았는데, 채택 설정은 셋 다 0 이었다.
| 항목 | 설계 문서 | 채택 설정 |
|---|---|---|
| 슬리피지 | 10bps — “없으면 백테스트가 거짓말을 함” | 0 |
| 최소 거래대금 | $1M — “없으면 체결 불가능한 종목으로 수익을 만듦” | 0 |
| 최소 주가 | $5 — “페니스톡 스프레드가 수익률 왜곡” | 0 |
비용 모델(backtest/costs.py)의 기본값조차 slippage_bps = 10 인데 설정에서
명시적으로 0 으로 덮어썼다. 문서와 구현이 갈렸고, 그 상태로 27개 실험이 돌았다.
이것이 왜 큰가: 회전율이 리밸런싱당 67%, 연 2.68회전이므로 왕복 슬리피지 1%마다 연 2.68% 가 사라진다. 채택 전략의 2008년 이후 SPY 초과수익이 연 5.58%p 인데, 초소형주 스프레드가 1~5% 인 것을 감안하면 초과수익이 통째로 사라질 수 있는 크기다.
이 저장소의 규약은 “구현이 문서와 어긋나면 문서를 고치거나 구현을 고친다”이다. 어느 쪽도 하지 않아서, 문서에 이미 적혀 있던 경고를 아무도 읽지 않은 상태로 하루가 지났다. 낙관적인 방향의 어긋남은 스스로 드러나지 않는다 — 성과가 좋아 보이므로 의심할 이유가 생기지 않는다.
검증 실험을 §5.5 에 기록한다.
조합 탐색(팩터 20개)이 풀 히스토리에서 OOM 으로 두 번 죽었다. 진행 로그에는 폴드 번호만 있어서 메모리가 자라는 것인지 원래 높은 것인지 구분할 수 없었다 — 커널 로그가 알려주는 것은 “죽었다” 뿐이다. 계측부터 넣고 실데이터로 쟀다.
| 단계 | RSS | 승격 캐시 |
|---|---|---|
| 데이터 로드 | 2.70GB | 0개 |
| evaluator 구성 | 5.66GB | 5개 / 1,811MB |
| 폴드 1 (팩터 20개 평가) | 10.4GB | — |
폴드마다 새는 게 아니었다. 분기→일별 승격 캐시에 크기 제한이 없었다. 승격 프레임 하나가 362MB(9,000 거래일 × 6,895종목)이므로 팩터를 늘릴수록 선형으로 쌓인다 — 팩터 8개짜리 채택 전략은 살아남고 20개짜리만 죽은 이유다.
close.pct_change() 를 evaluator 에서 한 번만 만든다. 파라미터와 무관한
500MB 프레임을 평가 호출마다(폴드 19 × 시도 3 = 100회 이상) 새로 만들고 있었다.OOM 을 키운 별개 원인: pgrep 이 비면 시작 형태의 대기자를 두 개 걸어둬
같은 작업이 두 번 떴다. 15GB 머신에서 8.5GB 프로세스 둘. 대기 조건을 검사하지
않고 한 프로세스에서 ; 로 잇는 scripts/run_backtests.sh 로 교체했다.
| 도구 | 용도 |
|---|---|
annualized_calmar + --objective |
낙폭으로 나누는 목적함수 |
value 비중 스킴 |
HXZ 기준 가치가중 검증 |
--train-window |
롤링 윈도 학습(최근 시장 가중) |
--tickers-file (분석 커맨드) |
패널 축소 — 없으면 15GB 머신에서 OOM |
scripts/factor_lab.py |
10분할 + IC + 회전율 일괄 평가 |
research/regime.py |
추세×변동성 2×2 레짐 분류 |
holdings / opt-factor-tui |
운용 — 오늘 살 목록·매매 계획 |
hold_multiple |
매매 유예구간(no-trade band) |
| # | 전략 | CAGR | MDD | Sharpe | DSR | 판정 |
|---|---|---|---|---|---|---|
| A | S&P500 이력 · 12팩터 | 12.59% | −63.3% | 0.397 | 0.402 | 기각 |
| B | 마이크로캡 · 6팩터 | 6.84% | −54.6% | 0.186 | 0.161 | 기각 |
| C | 대형주 · 5팩터 (Sharpe 목적) | 19.21% | −53.7% | 0.645 | 0.910 | 경계 |
| D | 대형주 · 5팩터 (Calmar 목적) | 19.60% | −45.0% | 0.673 | 0.932 | 후보 |
| E | D + 200일 타이밍 | 14.44% | −20.9% | 0.664 | 0.978 | 후보 |
| F | 소형주 8팩터 (원본) | 13.92% | −63.8% | 0.513 | 0.844 | — |
| G | F + 200일 타이밍 | 16.90% | −23.7% | 0.756 | 0.992 | 채택 |
G 의 우위는 2008년에 집중돼 있다. 원본이 그해 −42%, 타이밍을 켜면 0%다. 대가는 2009년 반등 초입(+80% → +55%)이고, 그 교환에서 남는 쪽이 컸다. 부수 효과로 좌측 꼬리가 잘리며 왜도·첨도가 개선돼 DSR 이 0.844 → 0.992 로 올랐다.
레버리지 여유: 낙폭 한도 30% 기준 1.2배까지 → CAGR 18.71% · MDD −29.9% (증거금 이자 연 6.5% 차감).
| 시도 | 결과 | 이유 |
|---|---|---|
| 신규 팩터 22개 | 전부 탈락 | 직접 조립 10 + OAP 라이브러리 12. 문헌 t=8.71 이 우리 유니버스에서 −0.70 |
| 소형·중형 유니버스 4종 | Sharpe 0.07~0.33 | 대형주 0.66 대비 전패 |
| 레짐 조건부 가중 | 16.90 → 15.45% | 표본 부족·판정 지연 |
| 비중 최적화 (대형주) | 16.90 → 12.65% | 균등·역변동성·리스크패리티·BL 탐색 |
| 비중 최적화 (소형주) | 16.90 → 15.73% | 낙폭도 −23.7 → −29.8% 악화 |
| 종목 집중 10~20개 | CAGR 하락 | 자유도만 늘어 DSR 0.978 → 0.877 |
| 가치가중 (HXZ) | Sharpe −28% | 죽지는 않음. 알파가 중소형에 있다는 뜻 |
| 저변동성 슬리브 | 단조성 −0.98 | 우리 유니버스·기간에서 부호 반대 |
| 조합 탐색 — IC 기준 (20개 풀 → 상위 6개) | 16.90 → 15.83% | 아래 참조 |
| 조합 탐색 — 잔차 기여도 기준 | 16.90 → 15.81% | 아래 참조. 성장 팩터는 여기서도 0번 |
정교한 것 중 채택 전략을 이긴 것은 없다. 다만 기각에는 두 종류가 있다. 변동성 타게팅·비중 최적화는 명백히 나빴고(Sharpe 0.756 → 0.396 등), 조합 탐색·섹터 상한은 차이를 잴 수 없었다(부트스트랩 구간이 0 을 포함). 후자를 “이겼다”고 쓰면 없는 증거를 만드는 것이다.
200일 이평 하나가 낙폭을 −64%→−24%로 줄인 것만이 크고 분명한 개선이다. DeMiguel et al.(2009)의 “1/N 을 이기기”가 실질 기준선이라는 결과는 유지된다.
팩터 20개 풀에서 각 학습 구간 내 IC 상위 6개를 고르게 했다 (select_top_k).
룩어헤드 없이, 폴드마다 그 시점까지의 정보만으로.
| 조합 탐색 | 채택 전략 (8팩터 고정) | |
|---|---|---|
| CAGR | 15.83% | 16.90% |
| MDD | −22.8% | −23.7% |
| Sharpe | 0.705 | 0.756 |
| Calmar | 0.70 | 0.71 |
| DSR | 0.985 | 0.992 |
낙폭만 0.9%p 낫고 나머지는 전패다. 선택이 흔들려서 진 것이 아니다 — 19개 폴드
전부가 PER · POR · PFCR · MOM_12_1 · ROIC 다섯을 고정으로 골랐고 여섯 번째만
PGPR↔PBR 로 갈렸다. 학습 구간이 매년 늘어나는데도 선택이 안정적이었다.
핵심은 한 번도 선택되지 않은 것에 있다. 성장 팩터 4종(순이익·영업이익· 매출총이익·매출 YoY)은 19번 중 0번 뽑혔는데, 그것들을 포함한 조합이 더 좋다.
당시 가설: 개별 IC 는 팩터를 하나씩 세워놓고 재는 자라 겹치지 않는 정보를 얼마나 더하는지를 못 본다. 가치·품질은 비슷한 종목을 고르고 성장은 다른 축이니, IC 순으로 자르면 그 축이 통째로 사라진다 — 이 가설은 아래에서 기각됐다.
“겹침 때문에 성장이 밀렸다”는 위 가설을 그대로 시험했다. select_factors_residual
은 이미 고른 팩터에 횡단면 회귀하고 남은 잔차의 IC 로 다음을 고른다.
| CAGR | MDD | Sharpe | Calmar | DSR | |
|---|---|---|---|---|---|
| 채택 G (고정 8팩터) | 16.90% | −23.7% | 0.756 | 0.71 | 0.992 |
| IC 선별 | 15.83% | −22.8% | 0.705 | 0.70 | 0.985 |
| 잔차 선별 | 15.81% | −26.8% | 0.701 | 0.59 | 0.985 |
성장 팩터는 이번에도 19번 중 0번 뽑혔다. 겹침을 제거한 뒤에도 그렇다 —
가설이 틀렸다. 성장 팩터는 이 유니버스에서 개별로도 잔차로도 측정 가능한
기여가 없다. 선택은 IC 기준만큼 안정적이었다(PER·POR·PFCR·MOM_12_1·PBR
고정, 여섯 번째만 PGPR↔ROIC).
채택 vs IC 선별 t = 0.48 Sharpe차 +0.047 95%[−0.21, +0.30] 채택 승률 63%
채택 vs 잔차 선별 t = 0.49 Sharpe차 +0.054 95%[−0.18, +0.30] 채택 승률 67%
부트스트랩 2,000회. “고정 조합이 선별을 이겼다”고 말할 수 없다 — 차이를 잴 수 없을 뿐이다. 이것은 이 문서의 다른 기각들과 성격이 다르다. 변동성 타게팅은 Sharpe 0.756 → 0.396 으로 명백히 나빴다.
그래도 결론은 유지한다. 이유가 바뀔 뿐이다 — 성과로 우열을 못 가리면 다른 기준으로 고르며, 거기서 고정 조합이 앞선다:
성장 팩터 4종이 두 기준 모두에서 0번 뽑혔다는 사실은 남는다. 그것들이 기여한다는 증거는 없다. 없다는 증거도 없다 — 빼도 결과가 구분되지 않으므로.
| 유니버스 | 종목 | 팩터 | OOS Sharpe |
|---|---|---|---|
| 역대 S&P500 (대형) | 1,191 | D안 5팩터 | 0.673 |
| 중형 $300M~5B | 4,133 | D안 5팩터 | 0.334 |
| 중형 $300M~5B | 4,133 | 신규 4팩터 | 0.176 |
| 마이크로 $100M~1B | 2,654 | 6팩터 | 0.186 |
| 소형 $100M~2B + 타이밍 | 6,486 | 5팩터 | 0.073 |
10분할 지표는 정반대를 가리켰다 — 중형주에서 SIZE t 8.41·단조성 1.00. 그러나 분위 수익 상세를 보면 유니버스 평균 21일 수익이 1.441%(연 19%), SIZE 상위 분위가 3.592%(연 52%)로 실현 불가능한 크기다. 균등가중 소형주의 호가 스프레드 반동이 만든 값이며, 비용·포지션 제약이 들어간 walk-forward 가 정직한 답을 준다.
교훈: 분위 스프레드는 후보 선별 도구이지 채택 근거가 아니다.
위 §3.3 은 유니버스와 팩터를 같이 바꿔서 기여를 나눌 수 없었다. 여기서는 채택 전략을 고정하고 시총 밴드만 올린다. 프로토콜도 동일하다 — 학습 5년 · 24폴드 · 72시도 · 2002-12~2026-08 · 목적함수 Calmar.
| 시총 밴드 | CAGR | MDD | Calmar | Sharpe | DSR | 판정 |
|---|---|---|---|---|---|---|
| 채택 (기준) | 23.78% | −23.7% | 1.00 | 1.047 | 1.000 | 유의 |
| 밴드 ×2 | 18.47% | −25.2% | 0.73 | 0.837 | 0.999 | 유의 |
| 밴드 ×3 | 12.93% | −34.4% | 0.38 | 0.518 | 0.944 | 우연과 구분 불가 |
| 밴드 ×5 | 11.29% | −37.5% | 0.30 | 0.445 | 0.906 | 우연과 구분 불가 |
밴드는 채택 밴드($5M~$80M)를 배수로 넓힌 것이다. 설정은 configs/strategy_band_*.json.
단조롭게 무너진다. 밴드를 올릴수록 수익이 줄 뿐 아니라 낙폭이 같이 깊어진다 — 수익과 위험이 동시에 나빠지므로 “큰 종목은 수익이 낮은 대신 안전하다”는 통상적 교환이 여기엔 없다. 밴드를 3배로만 넓혀도 DSR 이 채택 관문(0.95)을 통과하지 못한다.
이것이 용량 제약의 직접 측정이다. 가치가중 시 Sharpe −28%(§3.2)는 간접 증거였고, 이 표는 같은 결론을 훨씬 강하게 말한다 — 알파는 밴드 최하단에 있고, 조금만 위로 올라가도 통계적으로 사라진다.
좋은 소식이 아니다. 자금이 커지면 밴드를 올리는 것 말고 선택지가 없는데, 그 순간 이 전략은 우연과 구분되지 않는다. 실행 가능한 규모의 상한이 취향이 아니라 측정된 값으로 존재한다는 뜻이다.
올리면 무너진다면 내리면 어떤가. 유니버스 안에 채택 하한 아래로도 종목은 있다 — 시점당 130~190종목(2005·2012·2019·2025 분기말 실측)이라 얇지만 20~30종목 보유에는 부족하지 않다.
| 조건 | 밴드 | 방어 장치 |
|---|---|---|
band_down06 |
×0.6 (양끝) | 없음 |
band_down04 |
×0.4 (양끝) | 없음 |
band_cap_half |
상한만 절반 | 없음 |
band_down06_guards |
×0.6 (양끝) | 슬립 50bps · 주가 $5 · 거래대금 $1M |
예상: 앞의 셋은 성과가 오른다. 그리고 그 개선은 이 저장소에서 가장 못 믿을 숫자다.
universe/filters.py가 하한을 “선택이 아니라 방어” 라고 적어둔 이유가 이것이다 — Hou·Xue·Zhang(2020)이 보인 대로 체결 불가능한 초소형이 극단 분위수를 채우면 균등가중 백테스트가 조용히 부풀려진다.
그래서 네 번째가 이 묶음의 존재 이유다. 같은 밴드를 방어 켠 채로 돌린다. 켜고도 남으면 진짜고, 켜면 사라지면 앞의 셋은 신기루다. 방어 없는 셋만 돌리고 “더 좋다”고 적는 것이 정확히 §2.2 가 저지른 실수다 — 성과를 좋아 보이게 만드는 실수는 의심할 이유를 만들지 않는다.
| 조건 | CAGR | MDD | Calmar | Sharpe | DSR |
|---|---|---|---|---|---|
| 채택 (기준) | 23.74% | −23.7% | 1.00 | 1.047 | 1.000 |
| 아래 ×0.6 | 27.25% | −33.9% | 0.80 | 1.045 | 1.000 |
| 아래 ×0.4 | 28.06% | −43.7% | 0.64 | 1.041 | 1.000 |
| 상한만 절반 | 26.70% | −37.0% | 0.72 | 1.011 | 1.000 |
| 아래 ×0.6 + 방어 | −13.10% | −98.6% | −0.13 | −0.225 | 0.002 |
CAGR 은 올랐는데 Sharpe 는 제자리고 Calmar 는 나빠졌다. 밴드를 내릴수록 수익률이 23.74% → 28.06% 로 오르지만 낙폭이 −23.7% → −43.7% 로 깊어져 Calmar 가 1.00 → 0.64 로 떨어진다. 오른 것은 전부 위험을 더 져서 산 것이고, 위험당 수익은 한 톨도 늘지 않았다 (Sharpe 1.047 → 1.041).
표에서 제일 큰 CAGR 을 고르면 제일 나쁜 전략을 고르게 된다 — §5.2 의 파라미터 앙상블과 같은 형태의 함정이다.
방어를 켜면 −0.225 · DSR 0.002 로 cost_guards 와 똑같이 무너진다.
아래로 갈수록 거래 가능한 종목이 더 줄어드니 당연한 결과이며, 위쪽 사다리와
합치면 그림이 완성된다: 알파가 있는 구간과 거래가 되는 구간이 어긋나 있다.
예상을 미리 적어둔 것이 여기서 값을 했다. “성과가 오를 것”이라는 예측이 맞았고, 그 오름세를 개선으로 읽지 않을 근거를 결과를 보기 전에 확보해뒀다.
밴드 ×1.6 조건은 2026-08-15 실행이 벤치마크(SPY) 적재 전에 시작돼 죽었고
(ValueError: 벤치마크 'SPY' 가격이 없어…) 2026-08-16 배치에서 재실행 중이다.
|spread_t| ≥ 2 · 단조성 ≥ 0.6 · 회전율 ≤ 0.15 — 셋 다 만족해야 승격.
단조성이 낮은데 IC 만 높으면 극단값 몇 개가 만든 신호다.
| 팩터 | 상승·평온 (212개월) | 하락·평온 (19) | 하락·격동 (49) |
|---|---|---|---|
MOM_12_1 |
+2.79 | −0.63 | −2.75 |
PER |
1.89 | +4.62 | 0.97 |
POR |
1.83 | +5.23 | 0.76 |
SIZE |
1.21 | +5.73 | +5.25 |
VOL_52W |
0.97 | +7.82 | −3.12 |
모멘텀은 상승·평온장 전용이며 하락·격동장에서 부호가 뒤집힌다 — Daniel & Moskowitz(2016)의 모멘텀 크래시가 재현된 것이다. 가치는 하락장에서 폭발한다. 저변동성은 레짐마다 부호가 반대라 전체 기간으로 재면 상쇄돼 사라진다.
현재 레짐은 상승·평온이며 2년째 유지 중이다(2025년 4~6월 격동 제외).
Chen & Zimmermann 오픈소스 라이브러리(331 시그널)에서 회계 기반 · 연간 리밸런싱 · |t|≥3 인 44개를 추려 12개를 구현했으나 전부 탈락했다.
이는 버그가 아니라 문헌이 예고한 현상이다 — C&Z 의 재현은 대부분 균등가중·마이크로캡 포함 조건이고, C&Z 자신이 대형·중형주 제한만으로 월 60bp → 40bp(약 1/3) 감소를 측정했다. 여기에 2004년 이후 구조적 감쇠가 겹친다.
전부 구현했다. 구현과 채택은 다르다 — 아래 판정 칸이 비어 있으면 코드는 있으나 walk-forward 로 검증하지 않았다는 뜻이다.
| # | 기법 | 구현 | 판정 |
|---|---|---|---|
| 1 | 비용 인지 — hold_multiple 매매 유예구간 |
✓ | 기각. 회전율 −23% 대신 수익 −0.86%p |
| 2 | 변동성 타게팅 (Moreira & Muir 2017) | ✓ | 기각. §5.1 |
| 3 | 파라미터 앙상블 — 상위 k개 평균 (--ensemble) |
✓ | 기각. §5.2 |
| 4 | 섹터 비중 상한 (max_sector_weight) |
✓ | 성과 중립. §5.3 |
전 구간 walk-forward (19폴드 / 시도 57회 / OOS 18.6년, 2026-08-15 실행):
| CAGR | MDD | Sharpe | Calmar | DSR | |
|---|---|---|---|---|---|
| 원본 (타이밍 없음) | 13.92% | −63.8% | 0.513 | 0.22 | 0.844 |
| 채택 G | 16.90% | −23.7% | 0.756 | 0.71 | 0.992 |
| 조합 탐색 | 15.83% | −22.8% | 0.705 | 0.70 | 0.985 |
| 이평 + 변동성 타게팅 | 14.33% | −23.6% | 0.646 | 0.61 | 0.977 |
| 변동성 타게팅만 | 10.43% | −44.1% | 0.396 | 0.24 | 0.784 ✗ |
| 섹터 상한 25% | 17.75% | −24.9% | 0.763 | 0.71 | 0.991 |
| 앙상블 k=3 | 18.47% | −30.6% | 0.773 | 0.60 | 0.992 |
변동성 타게팅만 쓰면 타이밍을 아예 안 한 것보다 나쁘다. 낙폭을 −63.8→−44.1% 로 줄였지만 수익을 3.5%p 내주고 Sharpe 가 0.513→0.396 으로 떨어졌다. DSR 0.784 는 관문(0.95) 아래다.
이평 위에 얹으면 연 2.6%p 를 내고 낙폭은 0.1%p 만 줄인다. 변동성이 15.7→14.6% 로 준 것이 실체다 — 위험을 줄인 게 아니라 포지션을 줄였다.
실현변동성은 63일 뒤를 보는 자다. 변동성이 오른 뒤에 노출을 줄이므로 떨어지고 나서 팔고 반등 내내 작게 들고 있는다. 초소형주는 변동성이 높고 오래 머물러 이 시차가 더 비싸다. 200일 이평은 방향을 보고, 이 유니버스에서는 방향을 맞히는 쪽이 크기를 맞히는 쪽보다 확실히 낫다.
낙폭 감소가 곧 개선이 아니다. 변동성 타게팅만 쓴 판은 낙폭을 20%p 줄이고도 Sharpe 가 떨어졌다. 위험 대비 보상이 나빠졌으니 레버리지로 되돌릴 수도 없다.
CAGR 18.47% · Sharpe 0.773 으로 표에서 가장 좋아 보인다. 그런데
수익률만 보면 이기고 위험으로 나누면 진다. 상위 3개 파라미터를 평균 내며
n_stocks 가 작은(집중된) 조합이 섞인 결과로 보인다 — 파라미터 선택의 잡음은
줄었지만 그 대가로 포지션이 커졌다.
CAGR 이 0.85%p 높지만 차이가 0 과 구분되지 않는다: 일별 차이 t = 0.77, Sharpe 차이 95% 구간 [−0.140, +0.111], 개선 확률 41%. 두 전략 상관 0.955 다. 연도별로도 2014(+9.6%p)·2025(+12.5%p) 두 해가 이득을 다 만들고 2009·2020· 2021 에 −3~−6%p 씩 잃는다 — 같은 전략에 얹힌 잡음이다.
기각하지 않는 이유는 목적이 다르기 때문이다. 섹터 상한은 성과 개선 장치가 아니라 아무도 선택하지 않은 매크로 베팅을 없애는 장치다. 이 결과가 말하는 것은 “그 위험을 성과 비용 없이 걷어낼 수 있다” 이며, 그 값은 18년 표본이 우연히 보여주지 못한 꼬리에 있다.
4번의 동기는 가정이 아니라 실측이다 — 채택 전략의 현재 보유 종목 중 32% 가 Technology 였다. 팩터는 섹터 중립화하는데 최종 포트폴리오는 아무도 보지 않았다. 섹터 쏠림은 팩터 베팅이 아니라 의도하지 않은 매크로 베팅이고, 금리 하나가 성과를 결정하게 된다.
학습 구간을 10년에서 5년으로 절반으로 줄였다. 검증 구간이 18.6년 → 23.7년, 폴드 19 → 24개, 시도 57 → 72회가 된다.
| 구간 | CAGR | MDD | Sharpe | Calmar | DSR | |
|---|---|---|---|---|---|---|
| 학습 10년 | 2007-12~ | 16.90% | −23.7% | 0.756 | 0.71 | 0.992 |
| 학습 5년 | 2002-12~ | 23.78% | −23.7% | 1.047 | 1.00 | 1.000 |
이 23.78% 를 성과로 내걸면 안 된다. 같은 구간으로 잘라 비교하면:
| 2007-12 이후만 | CAGR | Sharpe |
|---|---|---|
| 학습 10년 | 16.90% | 0.756 |
| 학습 5년 | 16.92% | 0.758 |
사실상 완전히 같다. 개선분 전부가 새로 들어온 2003~2007 에서 나왔고, 그 구간만 떼면 CAGR 53.02% · Sharpe 1.882 다. 소형주 대세 상승기였다는 사실이 숫자에 그대로 있다 — 전략의 실력이 아니라 구간의 선물이며 반복을 기대할 근거가 없다.
진짜 소득은 그 “완전히 같다” 쪽이다. 학습 표본을 절반으로 줄여도 겹치는 구간의 성과가 세 자리에서 같다는 것은, 이 전략이 학습 길이에 둔감하다는 직접적인 증거다. 과최적화된 파라미터는 학습 창을 바꾸면 흔들린다. 게다가 폴드가 늘어 DSR 관문이 높아졌는데도 DSR 1.000 이다.
§5.4 는 학습 창을 확장(과거 전부)에서 5년 최소로 바꾼 것이었다. 그럼
아예 직전 5년만 쓰면 어떤가 (--train-window 5). 오래된 시장을 버리고
최근에만 맞추는 셈이라, 과최적화라면 여기서 흔들려야 한다.
| 학습 창 | CAGR | MDD | 변동성 | Calmar | Sharpe | DSR |
|---|---|---|---|---|---|---|
| 확장 (채택) | 23.74% | −23.7% | 17.0% | 1.00 | 1.047 | 1.000 |
| 롤링 5년 | 24.51% | −25.9% | 18.2% | 0.95 | 1.024 | 1.000 |
거의 같다. 학습 표본을 절반으로 줄여도(§5.4), 오래된 구간을 통째로 버려도 결과가 세 자리에서 움직이지 않는다. 파라미터가 학습 구간에 맞춰 흔들리지 않는다는 직접적인 증거이며, 이 저장소에서 로버스트니스를 지지하는 두 번째 독립 관측이다.
바꾸지는 않는다 — 확장 창이 표본을 더 쓰고 결과가 같으므로 바꿀 이유가 없다.
채택: 측정 프로토콜을 학습 5년으로 바꾼다. 폴드가 많을수록 검증이 강해진다. 보고 성과는 2007년 이후를 유지한다. 전략이 바뀐 것이 아니므로 숫자를 올릴 이유가 없고, 유리한 구간을 더해 표제를 키우는 것은 이 저장소가 지켜온 기준과 어긋난다.
§2.2 의 어긋남을 수치로 확인하는 실험이다. 세 가지를 따로 돌린다 — 슬리피지를 올리는 것은 같은 종목을 더 비싸게 사는 효과이고, 유동성 필터를 켜는 것은 애초에 그 종목을 안 사는 효과라 섞으면 기여를 나눌 수 없다.
| 실험 | 슬리피지 | 최소 거래대금 | 최소 주가 | 사전 예상 |
|---|---|---|---|---|
| 채택 (기준) | 0 | 0 | 0 | CAGR 16.92% (2008~) |
cost_slip50 |
50bps | 0 | 0 | 연 −1.3%p |
cost_slip150 |
150bps | 0 | 0 | 연 −4%p → 13%대 |
cost_guards |
50bps | $1M | $5 | 예측 불가 — 유니버스가 바뀐다 |
예상을 미리 적는 이유는 결과를 보고 기준을 정하지 않기 위해서다.
cost_guards 가 가장 중요하다. 이것이 무너지면 알파가 거래 불가능한
종목에만 있었다는 뜻이고, 그러면 이 전략은 종이 위에서만 존재한다.
반대로 살아남으면 지금까지의 숫자를 훨씬 강하게 믿을 수 있다.
§3.4 가 이 실험의 예상을 어둡게 만든다. 밴드를 조금만 올려도 알파가 사라진다면, 거래 가능성 필터도 같은 방향의 절단이기 때문이다 — 최소 거래대금 $1M 은 사실상 밴드 하단을 잘라내는 것과 비슷하게 작동한다.
실행 이력. 2026-08-15 야간 배치는 이 셋을 돌리지 못했다(queue_costs.log
0바이트, 산출물 없음). 2026-08-16 16:18 에 scripts/run_backtests.sh 로 재실행 —
비용 3건 + 미완의 band_8_150·roll5 를 순차로 잇는다. 결과는 나오는 대로
이 절에 채운다.
| 실험 | CAGR | MDD | 변동성 | Calmar | Sharpe | DSR | 누적 |
|---|---|---|---|---|---|---|---|
| 채택 (방어 0) | 23.74% | −23.7% | 17.0% | 1.00 | 1.047 | 1.000 | 153.0x |
cost_slip50 |
22.27% | −25.4% | 17.4% | 0.88 | 0.955 | 1.000 | 115.4x |
cost_slip150 |
18.64% | −31.0% | 18.9% | 0.60 | 0.736 | 0.995 | 56.6x |
cost_guards |
−12.76% | −99.2% | 42.8% | −0.13 | −0.224 | 0.002 | 0.0x |
슬리피지는 문제가 아니었다. 150bps 라는 가혹한 가정에서도 DSR 0.995 로 관문을 넘는다. 사전 예상(연 −4%p → 13%대)보다도 나았다.
무너뜨린 것은 유동성 필터다. 슬리피지 50bps 만이면 Sharpe 0.955 인데, 같은 슬리피지에 최소 주가 $5 · 최소 거래대금 $1M 을 더하면 −0.224 가 된다.
MDD −99.2% 는 “성과가 나빠진” 수준이 아니다. 20종목 균등가중 포트폴리오는 구조적으로 99% 를 잃을 수 없다. 원인을 원본 벌크에서 직접 셌다 — 유니버스가 비었다.
| 분기말 | 밴드만 | +주가 $5 | +거래대금 $1M | 남는 비율 |
|---|---|---|---|---|
| 2005-12 | 1,335 | 654 | 27 | 2.0% |
| 2010-12 | 1,153 | 506 | 15 | 1.3% |
| 2015-12 | 911 | 467 | 21 | 2.3% |
| 2020-12 | 745 | 472 | 158 | 21.2% |
| 2025-12 | 891 | 283 | 43 | 4.8% |
방어를 켜면 유니버스의 98% 가 사라진다. 20종목을 채워야 하는데 후보가 15~43개뿐이니 “1,000개 중 상위 20개”가 아니라 “존재하는 전부”를 담게 된다. 팩터 전략이 아니게 되고, 그것이 −99% 의 정체다. (2020년만 158로 튀는 것은 코로나 거래량 급증이다.)
결론은 “알파가 가짜였다”가 아니라 “이 밴드와 유동성 필터가 서로 배타적이다” 이다. 초소형주가 일 거래대금 $1M 을 넘으려면 시총의 상당 비율이 매일 회전해야 하고, 그런 종목은 드물다. 전략이 죽은 것이 아니라 파라미터를 다시 뽑아야 한다.
타이밍이 “현금”이라고 말하는 구간에 현금 대신 지수 인버스를 들면 어떤가. 백테스트를 새로 돌릴 필요도 없다 — 이탈 구간은 벤치마크만으로 정해지므로 SPY 하나로 계산된다 (1997-12-31 ~ 2026-08-14, 일별 −1배).
| 결과 | |
|---|---|
| 이탈 일수 | 2,017일 (전체의 28.0%) · 에피소드 46개 |
| 전부 인버스로 갔다면 | 누적 −67.0% (현금이면 0.0%) |
| 인버스가 이긴 에피소드 | 4 / 46 (9%) |
이 타이밍 규칙은 자주 틀린다. 46번 빠져나오는데 그중 42번은 지수가 계속 올랐다. 현금은 그 오판에 아무 대가도 치르지 않지만 인버스는 매번 돈을 낸다. 번 것은 진짜 약세장 셋뿐이다 (2000–02 +11.7%, 2002–03 +14.9%, 2007–09 +19.7%). 셋을 다 합쳐도 나머지 43번의 출혈을 못 갚는다.
가장 뼈아픈 자리는 방향이 아니라 경로다.
| 구간 | SPY | 인버스 |
|---|---|---|
| 2020-02-28 ~ 06-01 (코로나) | +3.3% | −11.1% |
| 2025-03-11 ~ 05-16 | +6.3% | −8.3% |
지수가 올랐으니 인버스가 지는 건 당연한데, 낙폭이 지수 상승폭의 3배다. 일별 −1배 상품은 변동성이 클수록 복리로 깎이기 때문이다. 급락 후 급반등처럼 경로가 험할수록 심하고, 그건 정확히 타이밍이 이탈해 있는 구간의 성격이다.
레버리지 인버스(−2x·−3x)는 이보다 훨씬 나쁘다. 변동성 손실이 배수의 제곱으로 붙는다. 나스닥 기반은 SPY 보다 변동성이 커서 더 불리하나, 스토어에 QQQ 가 없어 수치로 확인하지 않았다 — 추정으로 적지 않는다.
덧붙여 이 전략의 알파는 초소형주 롱에 있다. 지수 인버스는 대형주 베타를 가리는 것이라 헤지로서도 대상이 어긋난다.
두 제약이 서로 반대로 움직인다. 같은 표본일로 방어를 켠 후보 수를 세고, 밴드 사다리(§3.4)의 DSR 과 나란히 두면 겹치는 구간이 보인다.
| 밴드 | 방어 켠 후보 수(최소) | DSR (방어 끈 상태) |
|---|---|---|
| 채택 ×1 ($5M~80M) | 15 — 20종목도 못 채움 | 1.000 |
| ×1.6 | 47 | 0.999 |
| ×2 | 33 | 0.999 |
| ×3 | 67 | 0.944 — 관문 탈락 |
| ×5 | 114 | 0.906 — 관문 탈락 |
| ×10 | 236 | (미측정) |
아래로는 거래가 안 되고 위로는 알파가 사라진다. 겹치는 곳은 ×1.6~×2 뿐이다.
v2_band16_guards · v2_band20_guards 를 방어 켠 채로 돌리려 했으나
2026-08-16 21:38 사용자 요청으로 중단했다 (§5.8 의 E안 재측정을 우선).
설정은 configs/strategy_v2_*.json 에 있고 산출물은 없다.
결론이 나지 않은 항목이다. 위 DSR 은 방어를 끈 상태의 값이므로 ×1.6~×2 구간이 방어를 켜고도 살아남는지는 모른다. 추론으로 채우지 않는다.
5.8 E안 재측정 — 판정이 뒤집혔다 (2026-08-16)
§3.1 에서 E안(대형주 5팩터 + 200일 타이밍)은 G안에 밀려 후보로 남았다. 이유는 하나였다 — 수익률이 낮았다(14.41% vs 16.90%).
그런데 §5.5 가 밝힌 것은 G안의 그 우위가 방어 장치를 끈 상태의 값이라는
것이다. E안은 처음부터 방어가 켜져 있었다 (strategy_lean_timed.json —
최소 주가 $5 · 최소 거래대금 $1M · 슬리피지 15bps). 두 전략을 같은 조건으로
비교한 적이 없었던 셈이다.
새 프로토콜(학습 5년 · 24폴드 · 72시도 · 역대 S&P500 1,191종목)로 다시 쟀다.
| E안 (옛 10년/19폴드) | E안 (새 5년/24폴드) | G안 (방어 ON) | |
|---|---|---|---|
| CAGR | 14.41% | 16.34% | −12.76% |
| MDD | −20.9% | −24.3% | −99.2% |
| Sharpe | 0.664 | 0.727 | −0.224 |
| DSR | 0.978 | 0.996 | 0.002 |
| 방어 장치 | 켜짐 | 켜짐 | 켜짐 |
폴드가 19 → 24, 시도가 57 → 72 로 늘어 DSR 관문이 높아졌는데도 0.978 → 0.996 으로 올랐다. §5.4 의 학습 5년 전환이 G안에서 그랬듯 여기서도 표본을 늘린 쪽이 이겼다.
같은 조건으로 비교하면 판정이 뒤집힌다. E안 DSR 0.996 · G안 0.002. E안이 기각된 이유(“수익률이 낮다”)는 비교 자체가 성립하지 않는 상태에서 내린 판정이었다.
여기에 §5.5 가 잰 용량 제약이 더해진다 — G안은 실제 보유 종목의 일 거래대금 중앙값이 약 $45k 라 원금 상한이 1억 남짓이고, 그중 둘은 거래대금이 0 이었다. E안의 유니버스는 역대 S&P500 이라 용량 제약이 사실상 없다.
| G안 초소형주 | E안 대형주 | |
|---|---|---|
| 방어 켠 DSR | 0.002 | 0.996 |
| 원금 상한 | 약 1억 | 제약 없음 |
| 원금 2억 투입 시 | 실현 불가 | 연 3,270만원 (16.34% 기준) |
E안의 기본 가정은 슬리피지 15bps 다. 대형주치곤 타당하지만 가정은 가정이므로 50bps 로 올려 다시 쟀다.
| 슬리피지 | CAGR | MDD | 변동성 | Calmar | Sharpe | DSR |
|---|---|---|---|---|---|---|
| 15bps | 16.34% | −24.3% | 15.7% | 0.67 | 0.727 | 0.996 |
| 50bps | 14.91% | −24.3% | 15.7% | 0.61 | 0.648 | 0.988 |
CAGR 이 1.4%p 깎이지만 낙폭과 변동성은 그대로고 DSR 0.988 로 관문을 넘는다. 비용이 종목 선정을 바꾸지 않으니 위험 프로필이 유지되는 것이며, 이는 G안이 방어를 켜자 유니버스가 통째로 바뀌어 −99% 가 된 것과 정반대다.
운용 후보는 E안이다. 다만 표제 성과는 아직 바꾸지 않는다 — §5.7 의 v2 후보(방어 켠 초소형 밴드)가 미측정으로 남아 있고, 채택 전략을 교체하는 것은 그 결과까지 본 뒤에 할 판단이다. —
결론: 월별 · S=16 에서 PBO 0.303 · DSR 0.982(15bps)/0.957(50bps) — 관문 통과.
재현: uv run python scripts/strategy_search_cost.py (벤더 데이터 불필요).
여기까지 오는 데 정정이 두 번 있었다. 둘 다 남긴다.
오전에 35개 산출물을 재고 “DSR 0.988 · PBO 0.139” 라고 README 에 적었다. 그때 이 문서에는 아무것도 쓰지 않았고 재현 스크립트도 남기지 않았다. 저녁에 스크립트를 만들어 다시 재니 집계 주기와 블록 수에 따라 판정이 뒤집혔다:
일별(4,176행) S=8/10/12/16 → 0.657 / 0.524 / 0.599 / 0.544 전부 탈락
월별(201개월) S=8/10/12/16 → 0.314 / 0.155 / 0.294 / 0.278 전부 통과
seed 0~5 에서 소수점 셋째 자리까지 동일하다 — 난수 잡음이 아니다. 적어둔 0.139 는 열여섯 조합 중 최소값이었고, 그 사실이 어디에도 없었다.
정정 1 에서 관문 통과 주장을 통째로 철회했는데, 틀린 것은 숫자였지 결론이 아니었다. 어느 주기가 옳은지를 재보지도 않고 철회한 것이다.
CSCV 원논문(Bailey·Borwein·López de Prado·Zhu 2016)이 기준을 준다 — Sharpe 를 쓴다면 “IID Normal 가정이 성과의 여러 조각에서 유지될 수 있어야” 하고, 블록은 계열상관 구조가 보존될 만큼 커야 하며 S=16 이 대부분의 경우 적당하다.
네 주기를 그 기준으로 실측했다.
| 주기 | 관측 | VR−1 절댓값 중앙 (독립성) | 초과첨도 중앙 (정규성) | PBO (S=16) |
|---|---|---|---|---|
| 일별 | 4,176 | 0.068 최상 | 16.29 최악 | 0.544 탈락 |
| 주별 | 835 | 0.092 | 13.19 | 0.538 탈락 |
| 월별 | 198 | 0.109 양호 | 8.90 양호 | 0.303 통과 |
| 분기별 | 66 | 0.345 최악 | 2.79 최상 | 0.675 탈락 |
두 조건을 동시에 만족하는 것은 월별뿐이다. 일별은 초과첨도 16 으로 Sharpe 추정 자체가 성립하지 않고, 분기별은 정규성이 가장 좋지만 lag-1 자기상관 0.218 로 독립성이 깨지며 관측이 66개다.
분산비 VR(21)이 구성마다 0.78~1.45 로 다르다 (35개 중 20개가 1.2 초과). VR>1 은 일별이 위험을 과소평가한다는 뜻이므로, 일별 Sharpe 로 35개를 줄세우면 각자 다른 크기로 부풀린 값을 비교하게 된다.
증거는 VR 과 순위변동의 상관 +0.659 다:
| 구성 | VR(21) | 일별 순위 | 월별 순위 |
|---|---|---|---|
quantus_ens3 |
1.44 | 6위 | 15위 |
lean_timed_train5 (E안) |
0.99 | 13위 | 4위 |
lean_timed_slip50 |
0.99 | 20위 | 10위 |
E안은 √T 확장이 거의 정확히 성립하는데(VR 0.99) 경쟁 구성들의 일별 Sharpe 가 부풀려져 있어, 일별로 재면 E안이 부당하게 밀린다.
band_down06_guards 90%). 이것이 원인이라 보고 고비율
구성을 뺐더니 일별 PBO 가 오히려 나빠졌다 (0.516 → 0.579).max_splits 기본값 252 는 C(16,8)=12,870 중 일부만 돈다
(PBO 0.278). 전수로 가면 0.303 에서 수렴한다. 표제값은 전수다.월 경계 오프셋 21개에서 최악값이 0.468 이다. 관문(0.5)을 넘지만 여유가 크지
않다. 그리고 이 PBO 는 35개가 서로 다른 유니버스(초소형·중형·대형)를 쓰는
이질적 구성이라는 점을 보정하지 않는다 — CSCV 는 본래 한 전략의 파라미터 변형을
전제한다. 이 한계를 없애는 길은 하나뿐이다: 구성 하나를 미리 골라 단일 가설로
사전등록하는 것. docs/taa/01-results.md §6 이 TAA 쪽에 같은 처방을 적었다.
# 데이터 (이미 적재됨)
opt-factor status --store ~/data/us_micro.duckdb
# 공식 성과
opt-factor optimize --store ~/data/us_micro.duckdb \
--config ~/data/configs/strategy_quantus_timed.json \
--space ~/data/configs/space_small.json \
--tickers-file ~/data/universe_quantus.txt \
--method grid --trials 3 --min-train-years 5 --objective calmar
# 오늘 살 목록
opt-factor holdings --store ~/data/us_micro.duckdb \
--config ~/data/configs/strategy_quantus_timed.json \
--tickers-file ~/data/universe_quantus.txt
# 운용 화면
opt-factor-tui --store ~/data/us_micro.duckdb \
--config ~/data/configs/strategy_quantus_timed.json \
--tickers-file ~/data/universe_quantus.txt
# 전략 탐색 35회의 비용 (§6.1) — 벤더 데이터 없이 재현된다
uv run python scripts/strategy_search_cost.py
공식 성과의 정의는 optimize(walk-forward + DSR) 하나뿐이다.
단일 백테스트 수치는 어떤 경우에도 성과로 보고하지 않는다.