ETF 기반 포트폴리오 분석 툴

# ETF 포트폴리오 분석 대시보드

**한 줄로:** 보유한 ETF를 **구성종목까지 뚫고 들어가** 실제로 무엇에 얼마나 걸려 있는지 보여주는 도구.

---

# 1. 목적 — 왜 만들었나

## 1.1 해결하려는 문제

**ETF는 속을 보여주지 않는다.**

ETF를 여러 개 나눠 담으면 분산됐다고 느낀다. 성격이 다르고 이름이 다르니까. 그런데 ETF 안에는 수백 개 회사가 들어 있고, **서로 다른 ETF가 같은 회사를 겹쳐 담는다.** 그 겹침은 어디에도 표시되지 않는다.

아래 네 종목을 보유했다고 하자.

| 종목 | 성격 | 비중 |
|---|---|---|
| VOO | 미국 대형주 | 44% |
| QQQ | 미국 성장주 | 21% |
| SCHD | 미국 배당주 | 24% |
| KODEX 200 | 한국 대형주 | 11% |

겉으로는 네 갈래 분산이다. 구성종목까지 열어보면 다르다.

| 겉보기 | 실제 |
|---|---|
| ETF 4종에 분산 | **VOO ↔ QQQ 61%가 같은 종목** |
| 정보기술 21% | **정보기술 34%** |
| 집중도 낮음 | 한 단계 높음 |
| — | **엔비디아 한 종목에 8.2%** (직접 매수한 적 없음) |

## 1.2 왜 이게 위험한가

분산의 목적은 **같이 움직이지 않는 것**을 담는 것이다. VOO와 QQQ가 61% 같은 종목이면, 반도체가 빠질 때 **둘 다 같이 빠진다.** 두 개를 산 대가(수수료·관리 부담)는 치렀는데 얻으려던 효과는 못 얻는다.

손으로 확인하려면 ETF마다 구성종목 파일을 받아 맞춰봐야 한다. 종목이 500개를 넘으면 사실상 불가능하다.

## 1.3 시중 서비스와 다른 점

대부분의 포트폴리오 도구는 **ETF 단위**까지만 본다. 이 대시보드는 **한 겹 더 들어간다.**

| | 일반적인 도구 | 이 대시보드 |
|---|---|---|
| 분석 단위 | ETF | **구성종목** |
| 종목 겹침 | 안 보임 | 자동 탐지 |
| 못 본 데이터 | 대개 무시 | **금액·사유까지 표시** |
| 판정 | "이 ETF는 우수" | 판정하지 않음 (수치와 근거만) |

한국어 및 중국어 아이콘이 포함된 대시보드의 스크린샷


## 1.4 설계 원칙

취향이 아니라 **틀린 판단을 막기 위한 장치**다.

### ① 모르는 것을 아는 척하지 않는다

자산의 82%만 확인됐으면 화면에 **"82%만 봤다"**고 쓴다. 나머지를 0으로 채우지 않는다.

> **0%와 "모른다"는 다른 얘기다.** 확인 못 한 부분을 0으로 채우면 쏠림이 실제보다 작아 보이고, 그 순간 이 도구는 위험을 **과소평가**하게 만든다.

분해된 부분만으로 100%를 만드는 재정규화 경로를 코드 수준에서 막아뒀다. 미분해분은 **별도 버킷**을 차지한다.

### ② 판정하지 않는다

"이 ETF는 우수", "이 지표는 주의" 같은 **평가를 하지 않는다.** 색으로 좋고 나쁨을 말하지도 않는다(등락의 적·청은 방향 표시이지 평가가 아니다).

비교 화면에서도 항목별 유리값만 표시하고 **종합 승자를 정하지 않는다.** 비용을 중시할지 배당을 중시할지는 사용자가 정할 몫이다.

### ③ 한계를 숨길 수 없게 한다

모든 수치에 그것이 **어느 방향으로 틀렸는지**를 말하는 고지가 붙는다.

> **생존 편향이 보정되지 않았습니다.** 무료 데이터로는 상장폐지 ETF를 열거할 수 없어 과거 시점 유니버스를 복원할 수 없습니다. **결과는 생존 종목에 유리한 방향으로 편향됩니다.**

핵심은 이것이다 — **"수익률 14%"와 "14%, 단 생존 편향으로 과대 추정"은 완전히 다른 정보다.** 그래서 이 고지는 접거나 숨길 수 없다.

## 1.5 명시적 비목표

- **투자 자문·매매 권유** — 정보 제공 도구다
- **주문 실행·계좌 연동** — 화면에서 승인해도 실제 주문은 나가지 않는다
- **미래 수익 예측** — 자산 성장은 예측이 아니라 **가정을 입력받는 시나리오**로만
- **차단한 소스 우회** — 접근을 막은 운용사 데이터는 수집하지 않는다
- **유료 데이터 구독** — 무료·공개 경로만 사용한다

---

# 2. 기능 — 무엇을 제공하나


## 2.1 X-Ray — 핵심 기능

**보유 ETF를 구성종목까지 관통해 실제 노출을 재집계한다.**

> 노출액 = ETF 평가액 × 그 ETF 안에서의 구성 비중

이걸 보유한 모든 ETF에 대해 계산하고 회사별로 합산하면, "나는 엔비디아에 8.2%가 걸려 있다"가 나온다.

| 기능 | 사용자가 보는 것 |
|---|---|
| **겉보기 ↔ 실제 토글** | 같은 화면에서 ETF 단위 분포와 관통 결과를 전환. **분산 착시가 깨지는 지점** |
| **축 4종** | 업종 · 지역 · 개별 회사 · 통화 |
| **기여 ETF 표시** | 각 노출이 어느 ETF에서 왔는지 |
| **커버리지** | 확인된 자산 비율 + 못 본 부분의 종목·금액·사유 |
| **집중도** | 관통 전후 대조. 겉보기 분산, 실제 집중인 경우가 드러남 |

### X-Ray가 답하는 세 가지 질문

1. **나는 실제로 무엇에 투자하고 있나** — 라벨이 아니라 실체
2. **정말 분산돼 있나** — 겹침과 집중도
3. **갈아타면 실제로 뭐가 달라지나** — 교체 시뮬레이션

### X-Ray가 하지 않는 것

| 안 함 | 이유 |
|---|---|
| 종목 추천 | "그래서 뭘 사라"는 말을 하지 않는다 |
| 좋고 나쁨 판정 | 집중도가 높다고 나쁜 게 아니다. 의도한 집중일 수 있다 |
| 미래 예측 | 지금 상태만 보여준다 |

**"지금 어떤 상태인가"에 답하지, "뭘 해야 하나"에 답하지 않는다.**

## 2.2 중복 탐지 · 오버랩

- **겹쳐 담긴 회사** — 둘 이상의 ETF에 동시 포함된 종목, 실노출 금액, 출처 ETF와 각각의 비중
- **ETF 쌍별 중복도** — 가중 교집합으로 계산. 50% 초과 시 "사실상 같은 노출"

## 2.3 교체 시뮬레이션

"A를 팔고 B를 사면 어떻게 되나"를 **거래 전에** 계산한다. 실제 데이터는 바뀌지 않는 순수 계산이다.

비교 항목: 업종 실노출 · 집중도 · 특정 종목 중복 · 가중 총보수 · 배당수익률

## 2.4 대안 비교
다양한 그래프를 보여주는 웹페이지 스크린샷

- **관점 프리셋 5종** — 개요 / 비용·규모 / 성과 / 리스크 / 배당. 보고 싶은 기준으로 열이 바뀐다
- **최대 4개 나란히** — 항목별 유리값 표시(종합 판정 없음)
- **내 포트 중복도 열** — 후보가 이미 가진 것과 얼마나 겹치는지. **갈아타도 분산이 안 늘어나는 경우를 사전에 걸러낸다**

## 2.5 포트폴리오 — 지휘 화면

| 구역 | 내용 |
|---|---|
| **보유·배분** | 총평가액, 자산군 도넛, 종목별 **현재 vs 목표 비중** + 이탈 폭 |
| **진단** | 수익률(운용 성과 / 내 돈 기준 **분리**), 위험 대비 성과, 하루 최대 손실, 변동성, 배당수익률 |
| **손익 분해** | 이익을 **주가로 번 것 / 환율로 번 것**으로 분리 |
| **트래킹** | 가치 추이 + 매수·매도·배당 마커, 시간순 변화 기록 |
| **검토 목록** | 각 화면에서 발견된 사항이 모이는 곳 |

수익률을 **두 가지로 나눠** 보여주는 것이 특징이다. 하나는 입출금 영향을 뺀 순수 성과(종목 선택이 좋았나), 다른 하나는 언제 얼마를 넣었는지까지 반영한 실제 수익(내 돈이 얼마 벌었나).

## 2.6 배당

- 월별 실적 + 12개월 예상 (**예상분은 흐리게** 구분)
- **세전 / 세후 병기**, 원천징수액 표시
- Yield on Cost (분모가 매입원가임을 명시)
- 종목별 기여도, 수령 원장

## 2.7 거래·원장

거래가 **모든 계산의 출발점**이다. 입력하면 평균단가·보유수량·손익이 다시 계산된다.

- 매수·매도 입력 (소수주 지원)
- 달러 거래는 **그날 환율로 고정** 기록 → 나중에 환율이 변해도 매입원가 불변 → **주가 손익과 환차익을 분리**할 수 있다
- 증권사 CSV 임포트 (컬럼 매핑 → 미리보기 → 중복 감지 → 커밋)

## 2.8 종목 프로필 · 전략 · 테마
중국어로 된 거래 대시보드 스크린샷

| 화면 | 내용 |
|---|---|
| **종목 프로필** | 미국 ⇄ 한국 전환, 일봉/월봉 나란히 + 지수·환율 비교 오버레이, 기간 수익률, 리스크 지표, 섹터·상위 보유 |
| **전략** | 전략 라이브러리, 과거 성과, **검증 조건**(통과 못 한 항목도 그대로 표시), 연도별 수익률, 내 포트 적합도 |
| **테마** | 테마별 시장 규모·상대강도, **내 포트의 테마 노출 갭**(과다/적정/미보유) |

## 2.9 데이터 수집

무료·공개 경로만 사용한다. 실측 결과다.

| 소스 | 확보 | 비고 |
|---|---|---|
| **iShares** | 전체 구성종목 + **섹터** | 일 단위 |
| **SSGA** | 전체 | 일 단위 |
| **Vanguard** | 전체 | 월 단위 |
| **삼성 KODEX** | 전체 + 과거 3년 | 국내 |
| **한투 ACE** | 전체 + **섹터** | 국내 유일 섹터 제공 |
| **미래에셋 TIGER** | 전체 | 국내 |
| Invesco · Schwab | ❌ 차단 | 우회하지 않음 |

**교차 검증:** 같은 지수를 추종하는 두 소스(SPY·IVV)를 대조한 결과 최대 괴리 **0.011%p**.

### 수집 시 다루는 함정

실측에서 확인해 계약으로 못 박은 것들이다.

| 함정 | 대응 |
|---|---|
| 잘못된 식별자로 요청하면 **정상 응답으로 다른 펀드**가 옴 | 응답의 펀드명을 기대값과 대조 |
| 비중이 반올림돼 **롱테일 수천 행이 0.00**으로 보고됨 | 평가금액으로 재계산 |
| 휴장일 요청 시 **조용히 다른 날짜** 데이터 반환 | 응답에 실린 기준일을 검증 |
| 파생·커버드콜형은 **비중 합이 100%가 아님**(116%인 경우도) | 재정규화하지 않고 잔여로 분리 |
| 차단당해도 **오류 없이 빈 결과** 반환하는 경로 존재 | 상태코드가 아니라 **응답 구조**로 판별 |
| 해외 구성종목 코드가 티커가 아닌 **별도 표기 형식** | 정규화가 추정임을 화면에 표시 |

## 2.10 표시 규약

- **등락 색상** — 국내 관례(상승 적·하락 청). 평가색과 분리
- **기준일** — 소스마다 다르므로 항목별 표기
- **세전 기준** — 별도 표기 없으면 모두 세전
- **밝은 테마** 단일

---

# 3. 사용 흐름 — 어떻게 쓰나

실제로는 이 순서로 돌아간다.

```
① 거래 입력          매수·매도를 기록하거나 증권사 CSV를 올린다
      ↓              → 평단·보유수량·손익이 계산된다
② 포트폴리오          전체 상태를 본다. 비중이 목표에서 얼마나 벗어났나
      ↓
③ X-Ray             속을 열어본다. 겉보기와 실제가 얼마나 다른가
      ↓              → 여기서 대부분의 "몰랐던 것"이 나온다
④ 중복·집중도         어떤 회사가 겹쳤나. 어느 ETF끼리 사실상 같은가
      ↓
⑤ 대안 비교          겹치지 않는 후보를 찾는다 (중복도 열이 핵심)
      ↓
⑥ 교체 시뮬레이션      갈아타면 실제로 뭐가 달라지나 — 거래 전에 확인
      ↓
① 로 복귀            결정했으면 거래로 기록한다
```

**각 화면 하단에 "다음 단계" 안내**가 있어서 이 흐름을 따라갈 수 있다. 링크 문구는 화면 이름이 아니라 **목적**으로 쓰여 있다 — "X-Ray"가 아니라 "X-Ray로 진짜 노출 확인".

### 대표적인 사용 시나리오

**"내가 분산이 잘 돼 있나?"**
→ ③ X-Ray에서 **겉보기 ↔ 실제** 토글 한 번. 업종 비중이 크게 뛰면 착시였던 것.

**"ETF를 하나 더 살까?"**
→ ⑤ 대안 비교에서 후보의 **내 포트 중복도** 확인. 90%가 넘으면 사도 분산이 안 는다.

**"둘 중 뭘 팔지?"**
→ ④에서 중복도를 보고, ⑥에서 각각 팔았을 때의 변화를 비교.

---

# 4. 현재 한계

정직하게 밝히는 부분이다.

| 한계 | 내용 |
|---|---|
| **국내 커버리지 약 50%** | 삼성·미래에셋·한투 3개 운용사만 확보. 나머지는 **"불가"가 아니라 "미조사"** |
| **차단된 운용사** | Invesco·Schwab은 접근이 막혀 있고, 우회하지 않기로 했다 |
| **해외 종목 코드 정규화** | 국내 ETF의 해외 보유 종목은 코드 형식이 달라 **문자열 파싱 기반 추정**이다. 화면에 그 사실을 표시한다 |
| **섹터 근거 비대칭** | 미국은 발행사가 종목별 섹터를 직접 준다. 국내는 한투 ACE만, 그것도 펀드 단위 롤업이다. **같은 막대로 보여도 정밀도가 다르다** |
| **기준일 혼재** | 소스마다 갱신 주기가 달라(일 단위 ~ 월 단위) 한 화면 안에서 기준일이 섞인다 |
| **현재 표시값은 시뮬레이션** | 전체 화면은 가상 데이터로 동작한다. 실데이터 X-Ray는 별도 화면 |
| **국내 팩터·배당 이벤트 지표** | 무료 데이터가 없어 제공하지 않는다 |

---

# 5. 보완 계획

## 5.1 우선순위 판단 기준

기능을 추가할지 정할 때 쓰는 기준이다. 순서대로 통과해야 한다.

1. **데이터가 실제로 있는가** — 만들기 전에 실측으로 확인한다. 없으면 만들지 않는다
2. **틀렸을 때 어떻게 드러나는가** — 조용히 틀리는 기능은 넣지 않는다
3. **판정을 요구하는가** — 좋고 나쁨을 말해야 하면 근거가 확보될 때까지 보류
4. **못 본 부분을 표시할 수 있는가** — 커버리지를 말할 수 없는 기능은 신뢰할 수 없다

## 5.2 곧 할 것

| 항목 | 왜 먼저인가 |
|---|---|
| **KODEX 수집기 수정** | 국내 최대 ETF가 빠지면 커버리지가 크게 준다 |
| **국내 커버리지 50% → 62%** | KB·한화·신한·키움 등 미조사. 앞선 세 곳이 각각 20분 내에 찾아졌으므로 가능성이 높다 |
| **시안 디자인 완전 이식** | 제약이 풀렸으므로 미룰 이유가 없다 |
| **실데이터 전면 전환** | 데이터 구조를 실제 소스 형태로 만들어뒀으므로 화면 수정 없이 교체 가능 |

## 5.3 검토 중

| 기능 | 쟁점 |
|---|---|
| **종목 상세 드릴다운** | 필요성 높음 — "엔비디아 8.2%"를 보고 더 파고들 곳이 없다 |
| **손익 분해 4분할** | 매입원가·평가손익·실현손익·환율효과 |
| **산식 공개 화면** | 모든 지표의 계산식·기준값을 열람·조정 |
| **리스크-리턴 지도** | 표본 부족 종목을 제외했음을 명시해야 함 |
| **검토 목록(제안) 재설계** | 승인·판정 흐름이 "판정하지 않는다" 원칙과 긴장. **판정 근거가 확보될 때만 표시**하는 형태로 정리 필요 |

## 5.4 보류

| 기능 | 보류 사유 |
|---|---|
| **배당 12개월 예측** | 기대수익 제시에 가까워 **투자 자문 경계와 충돌**한다 |
| **국내 배당 이벤트 축** | 무계정 경로를 확인했으나 별건으로 분리 (검토 대기) |

## 5.5 하지 않기로 한 것

- **차단 운용사 우회** — 접근 차단은 명시적 의사표시로 본다
- **추측 기반 종목 매핑** — 식별자가 불확실한 데이터를 억지로 이어 붙이면 **중복 탐지가 조용히 틀린다.** 틀린 중복 정보는 없는 것보다 나쁘다
- **국내 팩터 모델** — 무료 데이터가 없다. 대체 지표로 근사하되 근사임을 표시

---

# 6. 실행 방법

## 6.1 시뮬레이션 대시보드 (8화면 전체)

```
app/dashboard.html
```

브라우저로 바로 열면 된다. 설치·서버 불필요. 데이터 파일(`app/sim.js`) 하나가 8개 화면을 전부 구동한다.

## 6.2 실데이터 X-Ray

```
app/fetch_xray.py    수집·재집계 → data.json
app/index.html       결과 표시
```

수집기를 실행하면 실제 운용사에서 구성종목을 받아 룩스루를 계산한다. **표준 라이브러리만 사용**하므로 별도 설치가 필요 없다.

보유 종목을 바꾸려면 수집기 안의 보유 목록을 고치고 다시 실행하면 된다.

## 6.3 파일 구성

| 파일 | 역할 |
|---|---|
| `app/dashboard.html` | 시뮬레이션 대시보드 (8화면) |
| `app/sim.js` | 화면 전체를 구동하는 데이터 |
| `app/fetch_xray.py` | 실데이터 수집기 |
| `app/index.html` | 실데이터 X-Ray 화면 |
| `mockups/` | UI 시안 v1~v3 (설계 원천) |
| `docs/` | 요구사항·설계 문서 |

**데이터와 화면이 분리돼 있다.** 데이터 구조를 실제 소스가 주는 형태 그대로 본떴으므로, 시뮬레이션을 실측값으로 교체할 때 화면 코드를 고치지 않는다.

---

## 고지

이 도구는 **정보 제공 도구이며 투자 자문이 아니다.** 모든 수치는 과거 데이터에 기반하고, 별도 표기가 없으면 세전이며, 미래 수익을 보장하지 않는다. 특정 종목의 매매를 권유하지 않는다.

현재 대시보드에 표시되는 값은 **시뮬레이션 데이터**다(실데이터 X-Ray는 별도 화면).

---

> **v2 개정 이력 (2026-08-12).** v1은 제작 과정을 서술한 사례글이었다. v2는 **목적 · 기능 · 보완 계획**으로 방향을 바꾸고, 여기에 **사용 흐름 · 현재 한계 · 실행 방법**을 추가했다. 제작 과정은 `AI_CASE_STUDY.md`, 작업 이력은 `DEVLOG_대시보드v3.md` 참조.
밀어주고 끌어주는

온·오프라인 AI 스터디

AI로 어디까지 할 수 있는지
직접 확인하실 분만 신청하세요.