HINDSIGHT 5조 스냅샷 1 / 20 GitHub서비스Swagger쇼릴데이터 흐름 영상

KAIST 디지털금융MBA · 클라우드컴퓨팅실습

5조

유튜버가 오른다고 한 종목,
정말 올랐을까?

HINDSIGHT

  • 김지환
  • 김현준
  • 오선호
  • 조인성
  • 채재희
주식·코인 유튜브 영상 목록을 보고 있는 캐릭터들
noun · /ˈhaɪnd.saɪt/ hindsight 뒤늦은 깨달음 — 지나고 나서야 보이는 것

영상이 맞았는지는 늘 지나고 나서야 안다.
그 "지나고 난 뒤"를 1년치 모았다.

목차

오늘 이야기할 순서

  1. 쇼릴1분 16초로 보는 HINDSIGHT
  2. 문제와 답왜 만들었나 · 무엇을 세나
  3. 데이터 · API공개 API 6개 · 채널 선정 · 파이프라인
  4. 아키텍처구조 · 요청 한 번의 흐름 · 막혔던 것과 고친 것
  5. 분석 결과정말 올랐을까 · 발견 · 한계
  6. 활용한 문장 · 누가 어떻게 쓰나
  7. 마무리부록(DB · API 문서) · 주소 3개

왜 만들었나

유튜브의 "오른다"는 전망,
적중률을 아무도 재지 않았다

애널리스트 리포트

있다적중률을 보여주는 서비스

주식 · 코인 유튜브

없다자극적인 제목 · 조회수 수십만

그래서 공개된 데이터를 모아 정제해서, 오늘 그 결과를 보여 드립니다

로켓에 올라타라고 외치는 유튜버 영상을 보는 캐릭터들

우리의 답

"오른다"고 한 영상 하나하나를 사건으로 보고,
그 뒤 주가를 전부 센다

YouTube제목 · 게시 시각
→
종목 · 논조"삼전" → 삼성전자 · 낙관만
→
시세 · 공시종가 · 거래량 · DART
→
5 · 20 · 60일수익률 · 시장 대비
51채널
45,660영상 제목 · 1년
19,016"오른다"고 한 언급
13,609종목 언급의 20거래일 성적표
(업종 언급 4,060건은 따로 · 최근 1개월은 아직)
홈 · 지금 뜨는 종목언급 뒤에 · 전체 성적표채널 · 누가 맞았나종목 · 주가 위 언급 시점데이터 · 출처와 가공

데이터 · API ① 출처

공개 API 6개, 각각 다른 질문에 답한다

YouTube Data API v3Google

누가 언제 무슨 종목을 말했나

채널 · 제목 · 게시일 · 조회수
DART 기업코드금융감독원

"삼전"이 어느 종목인가

상장사 3,998개 → 종목 사전
주식시세정보 V2공공데이터포털 · 금융위원회

그 뒤 주가는

국내 종가 · 거래량
Yahoo FinanceYahoo

시장이 오른 덕인가

미국 시세 · 코스피 · S&P500 · 업종 ETF 20
업비트 Open API두나무

코인은

KRW 마켓 294 · 일봉
DART 공시검색금융감독원

공시를 따라간 건가

공시 49,099건

데이터 · API ② 채널 선정

채널은 사람이 고르지 않았다

547YouTube 검색 후보
→
272구독자 · 업로드 · 쇼츠 · 언급률 측정
→
51채택 · 주식 38 코인 13

A · 대형 영향력

100만구독자 이상 · 언급률 15% 이상

B · 종목 집중

30%언급률 이상 · 구독자 주식 5만 · 코인 10만 이상

언급률 = 최근 50편 제목 중 종목이 나온 비율 · 둘 중 하나만 넘으면 채택 · 30% 하나로 자르면 삼프로TV·김작가TV가 빠진다

데이터 · API ③ 파이프라인 · 매일 07:10 GitHub Actions

제목 한 줄이 통계가 되기까지

① 수집
—
영상 제목
② 사전
—
종목 · 코인 · 테마
③ 매칭 · 논조
—
종목이 잡힌 영상 비율 · 언급 중 낙관
④ 시세 · 공시
—
시세 행 · 공시
⑤ 수익률
—
언급마다 5 · 20 · 60일 · 전 논조
매칭 26% → 65% · 업종 테마 사전사건일 = 영상 뒤 처음 거래할 수 있는 날오탐은 규칙 + 테스트 18개

아키텍처 ① 구조

Vercel · Render · Supabase · GitHub Actions

외부 API 6개 YouTube Data API DART 기업코드 · 공시 공공데이터포털 시세 Yahoo Finance 업비트 Open API 키는 환경변수에만 Render · FastAPI · 한 프로세스에 서비스 4개 market-data사전 · 시세 · 공시 youtube채널 · 영상 제목 mentions제목 → 종목 · 논조 stats수익률 · 시장 대비 · 랭킹 스키마 4개 · OpenAPI 명세 4개 · 서로는 REST 로만 읽기 GET /v1/* · 쓰기 /internal/sync (토큰) 무료 512MB → 프로세스 5개를 1개로 (95MB) Supabase · PostgreSQL 스키마 4개 · 테이블 9개서비스 사이는 FK 없이 id 만 Vercel · React 홈 · 언급 뒤에 · 채널 · 종목 · 데이터GET 만 부른다 GitHub · Actions push → Vercel · Render 자동 배포매일 07:10 /internal/sync ① → ⑤

아키텍처 ② 동작 원리

읽기는 GET, 쓰기는 배치만 — 외부 API 는 요청 때 부르지 않는다

요청 한 번화면이 숫자 하나를 받기까지
브라우저 · ReactVercel · "언급 뒤에" 탭
GET /stats/v1/summary?scope=overall&horizon=20&stance=bull← JSON · 0.6KB · 캐시 10분
FastAPI · statsRender · 키 조립 bull:overall:20
SELECT value FROM stats.summaries WHERE key = …← 1 row
PostgreSQLSupabase · 미리 계산된 요약
하루 한 번매일 07:10 · 10/17 확정
GitHub Actionscron · 토큰은 Secrets
POST /{service}/internal/syncX-Internal-Token
FastAPI · ① → ⑤수집 → 사전 → 매칭 → 시세 · 공시 → 수익률
외부 API 6개 읽기 → INSERT쓰기는 여기뿐
PostgreSQLsummaries 8,673행 갱신

아키텍처 ③ 막혔던 것과 고친 것 · 실패한 실험의 원인과 개선 과정

세 번 막혔고, 세 번 고쳤다

1제목에서 투자 대상을 26%밖에 못 찾았다→사전이 손으로 적은 110개뿐 → DART 로 상장사 3,998개 전부 + 별칭 · 못 잡은 제목 1,304건을 읽으니 "반도체 급등"처럼 업종만 말한 제목 → 업종 테마 사전 20개(ETF 로 측정, 종목과 따로 집계) → 65%
2"급등했던 이유"가 낙관으로 잡혔다→지난 일을 설명하는 제목은 전망이 아니다 → 과거 단서만 있고 앞날 단어가 없으면 중립 → 1,284건 제외
3무료 서버 512MB 가 터졌다→서비스 4개 + 게이트웨이 = 프로세스 5개가 기본 350MB → 한 프로세스에 마운트 · DART 3,998건 스트리밍 파싱 → 95MB

그 밖에 Supabase 연결 15개 한도 · 공공데이터포털 "미등록 키"(옛 V1 주소) · 뉴스 크롤링을 넣었다 뺀 것 — 질문 주시면 설명

분석 결과 ① 스냅샷

"오른다"고 한 뒤, 실제로는

낙관 언급 뒤 20거래일 수익률 분포 · 파랑 하락 · 빨강 상승

분석 결과 ② 시장별 · 채널별

시장은 모두 같은 방향, 채널은 다르다

20거래일 뒤표본평균상승시장 대비거래량

시장 대비 TOP 3 · BOTTOM 3 (낙관 30건 · 10종목 이상)

분석 결과 ③ 발견

숫자가 말해 준 다섯 가지

—

—

—

—

—

—

—

—

—

—

학습용 통계 · 투자 권유 아님 · 1년 · 채널 51개 · 제목만 본 범위 안의 결과

분석 결과 ④ 한계

이 숫자가 말하지 않는 것, 두 가지

1제목만 봤다

자막·설명은 안 봤고, 낙관·비관은 모델이 아니라 단어 사전으로 가른다. 그래서 낙관 언급을 완벽하게 가려내지는 못한다 — 근거 단어를 화면에 그대로 보여 준다

2최근 1년 · 채널 51개의 표본이다

다른 해, 다른 채널이면 숫자는 달라질 수 있다. 더 많은 채널, 더 긴 기간으로 넓힐 필요가 있다

그리고 이 결과는 상관이지 인과가 아니다 — 유튜브가 내리게 한 게 아니라 이미 오른 뒤에 언급이 몰리는 구조 · 종가 기준 · 거래 비용 없음

데이터가 말하는 한 문장

유튜브가 "오른다"고 할 때는
이미 늦었다

42%낙관 언급 뒤 20일 상승 확률
−2.9%시장 대비
0.92배언급 뒤 ÷ 앞 거래량

그래서 — 영상이 말한 종목을 사기 전에, 그 종목이 전에 떴을 때 어땠는지 먼저 본다

활용 · 비즈니스 모델

누가, 어떻게, 무엇으로 지속하나

누가

  • 개인투자자 — 유튜브로 정보를 얻는 사람
  • 리서치 · 콘텐츠팀 — 출연자 · 인용 채널 고를 때

어떻게 · 사기 전 두 번 클릭

  • 종목 — 전에 떴을 때 어땠나
  • 채널 — 이 채널 말은 맞았나

수익 · 운영

  • 언급 급증 알림 구독 · 채널 적중 리포트 · 데이터 API
  • 운영비 ≈ 0 — 무료 티어 + 하루 1회 배치 (YouTube 쿼터 300유닛)

부록 ① DB 테이블 문서 · 전체 docs/db.md

스키마 4개 · 테이블 9개 · 영상 1 → 언급 n → 수익률 1

스키마.테이블주요 컬럼키
yt.channelschannel_id, title, handle, category(stock·crypto), subscriber_count, uploads_playlist_idPK channel_id
yt.videosvideo_id, channel_id, title, published_at, view_countPK video_id · FK channel_id → channels
market.assetsasset_id(시장:심볼), market, symbol, name, aliases(JSON), asset_type(stock·etf·crypto·index·theme), benchmark_id, tracked, corp_codePK asset_id
market.pricesasset_id, trade_date, close, volumePK id · UQ(asset_id, trade_date)
market.disclosuresrcept_no, asset_id, rcept_dt, report_nm, kind(실적·계약·자금조달·주요사항·지분·기타)PK rcept_no
mentions.mentionsvideo_id, channel_id, asset_id, matched_text, confidence, stance(bull·bear·neutral), stance_words, published_atPK id · UQ(video_id, asset_id)
mentions.unmatchedvideo_id, channel_id, title, published_atPK video_id
stats.event_returnsmention_id, asset_id, channel_id, market, kind, stance, benchmark_id, t0_date, t0_close, r5·r20·r60, x5·x20·x60, vol_ratio, near_disclosurePK id · UQ mention_id
stats.summarieskey(bull:overall:20 · channel:<id>:20 …), value(JSON: n, mean, win_rate, excess_mean, histogram …)PK key
yt.channels yt.videos mentions.mentions market.assets market.prices market.disclosures stats.event_returns 1 ─ n 1 ─ n n ─ 1 1 ─ n n ─ 1 1 ─ 1 스키마 사이는 FK 없이 id 만 보관(서비스 독립) · 같은 스키마 안은 FK

부록 ② API 설명 문서 · 전체 docs/api.md · 실행

GET 으로 읽고, 쓰기는 배치만 · 엔드포인트 36개(읽기 24 · 내부·상태 12) 중 주요 8개

기능요청입력값응답(JSON)
수익률 요약GET /stats/v1/summaryscope(overall·market:KRX·channel:<id>·asset:<id>) · horizon(5·20·60) · stance(bull){ key, value:{ n, mean, median, win_rate, excess_mean, vol_ratio_median, histogram[] } }
채널 랭킹GET /stats/v1/channels/rankinghorizon · metric(excess_mean·win_rate·asset_win_rate) · min_n=30 · min_assets=10{ top[], bottom[], all[] } 각 { channel_id, n, mean, excess_mean, win_rate }
종목 언급별 수익률GET /stats/v1/assets/{asset_id}/eventsasset_id (예 KRX:005930){ items:[{ mention_id, channel_id, stance, published_at, t0_date, r5, r20, r60, x20, vol_ratio, near_disclosure }] }
급증 종목GET /mentions/v1/mentions/trendingdays=7 · limit · stance{ items:[{ asset_id, mentions, prev_mentions, channels }] }
언급 목록GET /mentions/v1/mentionsasset_id · channel_id · stance · after_id(커서){ items:[{ video_id, asset_id, matched_text, stance, stance_words, published_at }], next_cursor }
종가·거래량GET /market-data/v1/assets/{asset_id}/pricesfrom · to · limit{ items:[{ trade_date, close, volume }] }
채널 목록GET /youtube/v1/channels—{ items:[{ channel_id, title, handle, category, subscriber_count }] }
수집 실행(배치 전용)POST /{service}/internal/sync헤더 X-Internal-Token · days · background{ added, updated, skipped[] } · 상태는 /internal/sync-status

공통: JSON · ISO 날짜 · 수익률은 소수(0.024 = 2.4%) · 오류는 RFC 9457 problem+json · 응답마다 X-Request-ID · GET 은 Cache-Control 10분 · 명세 contracts/*.yaml(Spectral · oasdiff CI)

마무리

감사합니다

공개 API 6개 → 하루 한 번 가공 → 한 질문에 답하는 숫자

5조 · 김지환 · 김현준 · 오선호 · 조인성 · 채재희

서비스 QR
hindsight-web.vercel.app