portfolio-research

실험 기록 — 무엇을 시험했고 무엇이 남았나

2026-08-11 ~ 08-15. 구독 개시부터 전략 채택, 그 이후 검증까지의 기록이다.

이 문서의 목적은 같은 실험을 두 번 하지 않는 것이다. 성공한 하나보다 기각된 스무 개가 더 많은 지면을 차지하는 이유가 그것이다.


0. 요약

   
채택 전략 미국 소형주 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)

1. 데이터 기반

1.1 적재 결과

테이블 행수 종목 기간
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 리더 버그 수정으로 이번에 처음 채워졌다.

1.2 생존편향 제거 확인

폐지 종목이 실제로 들어 있는지 직접 확인했다.

티커 회사 데이터 종료
ENRNQ 엔론 2004-11-17
AAMRQ 구 아메리칸항공 2013-12-05
ABKFQ 암박 2010-11-16

actions 에 폐지 19,227건 · 분할 12,916건이 1997년부터 기록돼 있다.

1.3 벤더 실측 — 구독 후 확인된 사실

항목 확인 내용
유료 플랜 폐지 종목 포함 (벤더 직접 회신, 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

1.4 적재 중 잡은 함정

전부 “조용히 잘못되는” 유형이었다.

  1. TICKERS 10,000행 절단 — 커서가 없어 단일 요청인데 유니버스가 18,000종목이다. 한도를 채운 응답을 절단으로 간주해 TruncatedDataError.
  2. DAILY 백만 달러 단위 — 벌크 CSV 경로에 환산이 빠져 있었다. 미환산 시 mcap 이 10⁶배 작아져 EV 팩터와 시총 필터가 전부 죽는다.
  3. ticker 결측 행 — DAILY 벌크 300만 행당 약 44건. NOT NULL 로 적재 전체가 죽던 것을 경고 후 제외로.
  4. PIT 위반 행 — SF1 벌크에 datekey < reportperiod 가 42,841행당 2행. 넣으면 look-ahead, 전량 폐기는 과하다 → 제외하되 로그, 1% 초과 시 중단.
  5. isdelisted 미정규화 — CSV 경로만 리네임이 빠져 폐지 여부가 통째로 NULL.
  6. accessible_tickers() 자체가 생존편향 — 최근 4개 분기 재무로 유니버스를 찾으므로 폐지 종목이 원리적으로 안 잡힌다.

2. 방법론에서 고친 것

2.1 DSR 단위 버그 — 판정을 뒤집은 결함

deflated_sharpe_ratiosr_var기간(일별) 단위로 요구하는데, 넘기던 값은 목적함수가 만든 연율화 Sharpe 의 분산이었다. 252배 부풀려진 분산 → SR₀ 가 √252 ≈ 15.9배 → DSR 이 성과와 무관하게 0 에 붙는다.

같은 수익률·같은 시도 횟수에서 DSR 0.000 vs 0.910.

이 버그 때문에 초기 두 전략을 “우연과 구분 불가”로 기각했고, 그 판정은 무효였다. 목적함수가 Sharpe 가 아니면(Calmar 등) 분산의 의미가 달라지므로 추정하지 않고 보수적 기본값(1/T)으로 후퇴한다.

2.2 문서가 필수라고 적은 방어를 실제로는 껐다

전수 검토(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 에 기록한다.

2.3 메모리 — 팩터 수가 곧 한계였다

조합 탐색(팩터 20개)이 풀 히스토리에서 OOM 으로 두 번 죽었다. 진행 로그에는 폴드 번호만 있어서 메모리가 자라는 것인지 원래 높은 것인지 구분할 수 없었다 — 커널 로그가 알려주는 것은 “죽었다” 뿐이다. 계측부터 넣고 실데이터로 쟀다.

단계 RSS 승격 캐시
데이터 로드 2.70GB 0개
evaluator 구성 5.66GB 5개 / 1,811MB
폴드 1 (팩터 20개 평가) 10.4GB

폴드마다 새는 게 아니었다. 분기→일별 승격 캐시에 크기 제한이 없었다. 승격 프레임 하나가 362MB(9,000 거래일 × 6,895종목)이므로 팩터를 늘릴수록 선형으로 쌓인다 — 팩터 8개짜리 채택 전략은 살아남고 20개짜리만 죽은 이유다.

OOM 을 키운 별개 원인: pgrep 이 비면 시작 형태의 대기자를 두 개 걸어둬 같은 작업이 두 번 떴다. 15GB 머신에서 8.5GB 프로세스 둘. 대기 조건을 검사하지 않고 한 프로세스에서 ; 로 잇는 scripts/run_backtests.sh 로 교체했다.

2.4 추가한 도구

도구 용도
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)

3. 전략 실험

3.1 채택까지의 경로

# 전략 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% 차감).

3.2 기각 기록

시도 결과 이유
신규 팩터 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 을 이기기”가 실질 기준선이라는 결과는 유지된다.

조합 탐색 — 개별 IC 가 못 보는 것

팩터 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번 뽑혔다는 사실은 남는다. 그것들이 기여한다는 증거는 없다. 없다는 증거도 없다 — 빼도 결과가 구분되지 않으므로.

3.3 유니버스 사다리

유니버스 종목 팩터 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.4 시총 밴드 사다리 — 용량 제약을 직접 잰다

위 §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)는 간접 증거였고, 이 표는 같은 결론을 훨씬 강하게 말한다 — 알파는 밴드 최하단에 있고, 조금만 위로 올라가도 통계적으로 사라진다.

좋은 소식이 아니다. 자금이 커지면 밴드를 올리는 것 말고 선택지가 없는데, 그 순간 이 전략은 우연과 구분되지 않는다. 실행 가능한 규모의 상한이 취향이 아니라 측정된 값으로 존재한다는 뜻이다.

반대편 — 밴드를 더 줄이면 (2026-08-16)

올리면 무너진다면 내리면 어떤가. 유니버스 안에 채택 하한 아래로도 종목은 있다 — 시점당 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 배치에서 재실행 중이다.


4. 팩터 분석

4.1 10분할 승격 기준

|spread_t| ≥ 2 · 단조성 ≥ 0.6 · 회전율 ≤ 0.15 — 셋 다 만족해야 승격. 단조성이 낮은데 IC 만 높으면 극단값 몇 개가 만든 신호다.

4.2 레짐별 IC (×100, 역대 S&P500)

팩터 상승·평온 (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월 격동 제외).

4.3 문헌 대조

Chen & Zimmermann 오픈소스 라이브러리(331 시그널)에서 회계 기반 · 연간 리밸런싱 · |t|≥3 인 44개를 추려 12개를 구현했으나 전부 탈락했다.

이는 버그가 아니라 문헌이 예고한 현상이다 — C&Z 의 재현은 대부분 균등가중·마이크로캡 포함 조건이고, C&Z 자신이 대형·중형주 제한만으로 월 60bp → 40bp(약 1/3) 감소를 측정했다. 여기에 2004년 이후 구조적 감쇠가 겹친다.


5. 포트폴리오 구성 기법 — 구현 상태

전부 구현했다. 구현과 채택은 다르다 — 아래 판정 칸이 비어 있으면 코드는 있으나 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

5.1 변동성 타게팅 — 문헌이 재현되지 않았다

변동성 타게팅만 쓰면 타이밍을 아예 안 한 것보다 나쁘다. 낙폭을 −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 가 떨어졌다. 위험 대비 보상이 나빠졌으니 레버리지로 되돌릴 수도 없다.

5.2 파라미터 앙상블 — 표에서 제일 높고 실제로는 제일 나쁘다

CAGR 18.47% · Sharpe 0.773 으로 표에서 가장 좋아 보인다. 그런데

수익률만 보면 이기고 위험으로 나누면 진다. 상위 3개 파라미터를 평균 내며 n_stocks 가 작은(집중된) 조합이 섞인 결과로 보인다 — 파라미터 선택의 잡음은 줄었지만 그 대가로 포지션이 커졌다.

5.3 섹터 상한 — 성과로 판단할 항목이 아니다

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 였다. 팩터는 섹터 중립화하는데 최종 포트폴리오는 아무도 보지 않았다. 섹터 쏠림은 팩터 베팅이 아니라 의도하지 않은 매크로 베팅이고, 금리 하나가 성과를 결정하게 된다.

5.4 학습 5년 — 이 저장소에서 가장 강한 로버스트니스 증거

학습 구간을 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년으로 잘라도 같다 (2026-08-16)

§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년 이후를 유지한다. 전략이 바뀐 것이 아니므로 숫자를 올릴 이유가 없고, 유리한 구간을 더해 표제를 키우는 것은 이 저장소가 지켜온 기준과 어긋난다.

5.5 현실적 비용 — 검증 완료 (2026-08-16)

§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 를 순차로 잇는다. 결과는 나오는 대로 이 절에 채운다.

결과 (2026-08-16) — 슬리피지는 견뎠고 유동성 필터가 무너뜨렸다

실험 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 을 넘으려면 시총의 상당 비율이 매일 회전해야 하고, 그런 종목은 드물다. 전략이 죽은 것이 아니라 파라미터를 다시 뽑아야 한다.

5.6 이탈 구간에 인버스 — 기각 (2026-08-16)

타이밍이 “현금”이라고 말하는 구간에 현금 대신 지수 인버스를 들면 어떤가. 백테스트를 새로 돌릴 필요도 없다 — 이탈 구간은 벤치마크만으로 정해지므로 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 가 없어 수치로 확인하지 않았다 — 추정으로 적지 않는다.

덧붙여 이 전략의 알파는 초소형주 롱에 있다. 지수 인버스는 대형주 베타를 가리는 것이라 헤지로서도 대상이 어긋난다.

5.7 v2 후보 — 알파와 거래 가능성이 겹치는 구간 (미완)

두 제약이 서로 반대로 움직인다. 같은 표본일로 방어를 켠 후보 수를 세고, 밴드 사다리(§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 후보(방어 켠 초소형 밴드)가 미측정으로 남아 있고, 채택 전략을 교체하는 것은 그 결과까지 본 뒤에 할 판단이다. —

6. 알려진 한계

6.1 전략 탐색 35회 — 정산했다 (숫자는 두 번 고쳤다)

결론: 월별 · S=16 에서 PBO 0.303 · DSR 0.982(15bps)/0.957(50bps) — 관문 통과. 재현: uv run python scripts/strategy_search_cost.py (벤더 데이터 불필요).

여기까지 오는 데 정정이 두 번 있었다. 둘 다 남긴다.

정정 1 (2026-08-17 저녁) — 숫자를 가장 유리한 것으로 골랐다

오전에 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 는 열여섯 조합 중 최소값이었고, 그 사실이 어디에도 없었다.

정정 2 (2026-08-17 밤) — “관문을 못 넘는다”는 과했다

정정 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안이 부당하게 밀린다.

세운 가설 중 기각한 것

남은 한계

월 경계 오프셋 21개에서 최악값이 0.468 이다. 관문(0.5)을 넘지만 여유가 크지 않다. 그리고 이 PBO 는 35개가 서로 다른 유니버스(초소형·중형·대형)를 쓰는 이질적 구성이라는 점을 보정하지 않는다 — CSCV 는 본래 한 전략의 파라미터 변형을 전제한다. 이 한계를 없애는 길은 하나뿐이다: 구성 하나를 미리 골라 단일 가설로 사전등록하는 것. docs/taa/01-results.md §6 이 TAA 쪽에 같은 처방을 적었다.


7. 재현 절차

# 데이터 (이미 적재됨)
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) 하나뿐이다. 단일 백테스트 수치는 어떤 경우에도 성과로 보고하지 않는다.