본문 바로가기
시스템 트레이딩 소개/데이터 수집

기술적 분석의 첫 단추, 데이터 무결성 확보하기 (yfinance)

by 흔한트리이더 2026. 2. 7.
반응형
학습 목표
1. 복잡한 증권사 API 없이 파이썬으로 국내 주식(KOSPI, KOSDAQ) 데이터를 확보합니다.
2. 국내 종목 코드(6자리)를 야후 파이낸스 티커 포맷(.KS / .KQ)으로 변환하는 로직을 구현합니다.
3. 액면분할과 배당락으로 인한 데이터 왜곡을 이해하고, '수정 주가(Adj Close)'로 무결성을 확보합니다.

많은 분이 처음에는 증권사 HTS(Home Trading System)나 MTS가 제공하는 차트만으로 분석을 시작해요. 하지만 공부를 하다 보면 이동평균선의 기간을 내 마음대로 비틀어보고 싶거나, 남들은 모르는 나만의 보조 지표를 만들어보고 싶은 욕구가 생기죠. 이때가 바로 파이썬이라는 도구를 잡아야 할 시점이에요.

그런데 막상 파이썬으로 차트를 그리려 하면 첫 번째 관문에서 막혀요. 바로 "데이터를 어떻게 가져오느냐"는 것이죠. 키움증권이나 대신증권의 API를 연동하려면 32비트 환경 설정부터 복잡한 인증 절차까지, 분석을 시작하기도 전에 지치기 십상이에요.

우리는 '시스템 트레이딩(자동매매)'이 아니라, 우선 '기술적 분석(지표 산출)'이 목표잖아요? 그렇다면 훨씬 가볍고 강력한 대안이 있어요. 바로 전 세계 금융 데이터의 표준인 Yahoo Finance(yfinance) 라이브러리를 활용하는 것이죠. 오늘은 국내 주식 데이터를 깔끔하게 가져오는 법과, 그 과정에서 반드시 주의해야 할 '데이터의 함정'을 다뤄볼게요.


국내 주식의 특수성: 티커(Ticker)의 비밀

미국 주식은 애플(AAPL), 테슬라(TSLA)처럼 직관적인 알파벳 티커를 써요. 하지만 한국은 005930(삼성전자) 같은 6자리 숫자 코드를 사용하죠.

여기서 초보자들이 가장 많이 묻는 질문이 있어요.
"네이버 금융에서 크롤링하면 되지 않나요?"

물론 가능해요. 하지만 크롤링은 웹페이지 구조가 바뀌면 코드가 먹통이 되고, 자칫하면 IP가 차단될 수도 있어요. 반면 yfinance는 API 형태로 데이터를 제공하기 때문에 훨씬 안정적이죠.

다만 한 가지 규칙이 있어요. 야후 파이낸스는 전 세계 주식을 다루기 때문에, 이 숫자가 한국 주식인지 구분할 꼬리표(Suffix)가 필요해요.

  • 코스피(KOSPI): 종목코드 뒤에 .KS (예: 005930.KS)
  • 코스닥(KOSDAQ): 종목코드 뒤에 .KQ (예: 096530.KQ)

이 사소한 꼬리표 하나를 붙이지 않아서 "데이터가 없다"며 포기하는 경우가 정말 많답니다.


기술적 지표를 망치는 주범: 수정 주가(Adjusted Close)

데이터를 가져올 때 가장 중요한 개념을 짚고 넘어갈게요. 데이터 컬럼을 보면 Close(종가)와 Adj Close(수정 주가)가 따로 있어요.

만약 여러분이 분석하려는 기간 중에 해당 기업이 액면분할을 했거나 배당을 했다면 어떻게 될까요?
예를 들어, 주가가 10만 원인 주식이 1/2로 액면분할을 하면 가격은 5만 원이 돼요. 기업 가치는 그대로인데 차트상에서는 주가가 반 토막 난 것처럼 보이죠.

이때 Close 데이터를 그대로 사용해서 이동평균선(MA)을 그리면 어떻게 될까요? 분할 시점에서 주가가 급락한 것으로 인식되어, 이동평균선이 곤두박질치고 거짓 매도 신호(Fake Signal)가 발생해요. 이것이 바로 기술적 분석을 망치는 '데이터 왜곡'이에요.

"차트 분석에서 중요한 건 '그때 얼마였나'라는 가격 자체가 아니라, '추세가 이어지는가'라는 연속성이에요."

그래서 우리는 과거의 가격을 현재 기준으로 보정한 Adj Close를 기술적 지표 산출의 기준으로 삼아야 해요.


파이썬 구현: 코스피/코스닥 데이터 수집기

이제 이론은 충분하니 코드로 구현해 볼까요? 사용자가 종목 코드만 입력하면 알아서 접미사(Suffix)를 처리하고, 기술적 분석에 필요한 데이터만 깔끔하게 정리해 주는 함수를 만들었어요. 각 단계를 차근차근 따라와 보세요.

Step 1: 라이브러리 임포트 및 함수 정의

먼저 필요한 라이브러리인 yfinancepandas를 불러오고, 데이터를 수집하고 전처리할 get_technical_data 함수를 정의합니다. 이 함수는 종목 코드, 시장 타입, 기간을 입력받습니다.


import yfinance as yf
import pandas as pd

def get_technical_data(ticker_code, market_type='KOSPI', period='1y'):
    """
    국내 주식 데이터를 yfinance를 통해 수집하고
    기술적 분석에 최적화된 형태로 전처리하는 함수
    """
    

Step 2: 티커(Ticker) 생성

입력받은 시장 타입(market_type)에 따라 코스피는 .KS, 코스닥은 .KQ 접미사를 종목 코드 뒤에 붙여 yfinance에서 사용할 수 있는 전체 티커를 만듭니다.


    # 1. 시장 구분에 따른 접미사(Suffix) 결정
    suffix = '.KS' if market_type.upper() == 'KOSPI' else '.KQ'
    full_ticker = f"{ticker_code}{suffix}"
    
    print(f"[Info] 데이터 수집 시작: {full_ticker}")
    

Step 3: 데이터 다운로드 및 예외 처리

yf.download 함수를 사용하여 데이터를 다운로드합니다. 만약 데이터가 없으면(예: 잘못된 종목 코드) 에러 메시지를 출력하고 None을 반환하여 프로그램이 중단되는 것을 방지합니다.


    # 2. yfinance로 데이터 다운로드 (진행바 표시 끔)
    df = yf.download(full_ticker, period=period, progress=False)
    
    # 데이터가 비어있는 경우 예외 처리
    if df.empty:
        print(f"[Error] 데이터를 찾을 수 없습니다.")
        return None
    

Step 4: MultiIndex 처리

yfinance의 최신 버전에서는 데이터프레임의 컬럼이 다중 인덱스(MultiIndex)로 반환될 수 있습니다. 이를 해결하기 위해 불필요한 레벨을 제거하여 컬럼을 평탄화합니다.


    # 3. 기술적 분석을 위한 데이터 정제
    if isinstance(df.columns, pd.MultiIndex):
        df.columns = df.columns.droplevel(1)
    

Step 5: 수정 주가(Adj Close) 처리

기술적 분석에서 가장 중요한 단계입니다. 액면분할이나 배당으로 인한 가격 왜곡을 피하기 위해 Adj Close를 기본 가격(Price)으로 사용합니다. Adj Close가 없는 경우를 대비한 예외 처리도 포함되어 있습니다.


    # 4. 핵심: 단순 종가(Close) 대신 수정 주가(Adj Close)를 기본 가격으로 채택
    if 'Adj Close' in df.columns:
        df['Price'] = df['Adj Close']
    else:
        df['Price'] = df['Close'] # Adj Close가 없는 경우 Close 사용
    

Step 6: 최종 데이터 선택 및 반환

필요한 컬럼(시가, 고가, 저가, 수정 종가, 거래량)만 선택하여 최종 데이터프레임을 만들고 반환합니다.


    # 필요한 컬럼만 선택하여 깔끔하게 정리
    target_cols = ['Open', 'High', 'Low', 'Price', 'Volume']
    existing_cols = [col for col in target_cols if col in df.columns]
    df = df[existing_cols]
    
    print(f"[Success] {len(df)} 거래일 데이터 확보 완료")
    return df
    

위 과정을 모두 합친 전체 코드는 아래와 같아요. 복사해서 바로 실행해 보세요.

전체 소스 코드 확인하기 (Click)

import yfinance as yf
import pandas as pd

def get_technical_data(ticker_code, market_type='KOSPI', period='1y'):
    """
    국내 주식 데이터를 yfinance를 통해 수집하고
    기술적 분석에 최적화된 형태로 전처리하는 함수
    
    Args:
        ticker_code (str): 6자리 종목 코드 (예: '005930')
        market_type (str): 'KOSPI' 또는 'KOSDAQ'
        period (str): 데이터 수집 기간 (예: '1y', 'max', 'ytd')
        
    Returns:
        pd.DataFrame: OHLCV 데이터 (Adj Close 포함)
    """
    
    # 1. 시장 구분에 따른 접미사(Suffix) 결정
    suffix = '.KS' if market_type.upper() == 'KOSPI' else '.KQ'
    full_ticker = f"{ticker_code}{suffix}"
    
    print(f"[Info] 데이터 수집 시작: {full_ticker}")
    
    # 2. yfinance로 데이터 다운로드 (진행바 표시 끔)
    df = yf.download(full_ticker, period=period, progress=False)
    
    # 데이터가 비어있는 경우 예외 처리
    if df.empty:
        print(f"[Error] 데이터를 찾을 수 없습니다. 종목 코드나 시장 타입을 확인해주세요.")
        return None
    
    # 3. 기술적 분석을 위한 데이터 정제
    # 불필요한 레벨 제거 (yfinance 최신 버전 호환성)
    if isinstance(df.columns, pd.MultiIndex):
        df.columns = df.columns.droplevel(1)
        
    # 4. 핵심: 단순 종가(Close) 대신 수정 주가(Adj Close)를 기본 가격으로 채택
    # 기술적 지표 계산 시 왜곡을 방지하기 위함
    # 원본 Close는 보존하되, 분석용 컬럼을 별도로 생성
    if 'Adj Close' in df.columns:
        df['Price'] = df['Adj Close']
    else:
        df['Price'] = df['Close'] # Adj Close가 없는 경우 Close 사용
        
    # 필요한 컬럼만 선택하여 깔끔하게 정리
    target_cols = ['Open', 'High', 'Low', 'Price', 'Volume']
    # 컬럼 존재 여부 확인 후 필터링
    existing_cols = [col for col in target_cols if col in df.columns]
    df = df[existing_cols]
    
    print(f"[Success] {len(df)} 거래일 데이터 확보 완료")
    return df

# --- 실행 예시 ---
# 삼성전자(005930, 코스피) 데이터 1년치 가져오기
sec_data = get_technical_data('005930', 'KOSPI', '1y')

if sec_data is not None:
    print("\n[데이터 미리보기]")
    print(sec_data.tail()) # 최근 5일치 데이터 출력
        
*본 포스팅은 실무 경험을 바탕으로 작성된 기술 참고 자료입니다. 제공된 코드와 전략은 수익을 보장하지 않으며, 모든 투자에 대한 최종 결정과 책임은 투자자 본인에게 있습니다. 실제 매매에 적용하기 전 반드시 충분한 검증과 모의 투자를 거치시기 바랍니다.*
반응형