파이썬 pandas로 주가 데이터 다루기 기초 — CSV 읽기부터 이동평균까지

🌐 English

주가 데이터를 모았는데 그다음 뭘 해야 할지 모르겠다면, 답은 대체로 pandas입니다. 파이썬으로 표 형태 데이터를 다루는 라이브러리로, 자동매매·백테스트의 거의 모든 계산이 여기서 출발합니다. 이 글은 CSV 한 장으로 시작해 이동평균과 수익률까지 가는 최소 경로를 다룹니다.

⚠️ 이 글은 교육·정보 제공 목적입니다. 투자 권유가 아니며, 투자 손실 책임은 본인에게 있습니다.

1. 데이터 모양 잡기

주가 데이터는 보통 이런 CSV로 저장합니다. 날짜, 시가, 고가, 저가, 종가, 거래량 — 이 여섯 개면 충분합니다.

date,open,high,low,close,volume

2026-01-02,70100,71200,69800,71000,12345678

2026-01-03,71000,71500,70500,70600,9876543

pandas로 읽을 때 중요한 건 날짜를 진짜 날짜로 읽는 것입니다. 그냥 읽으면 문자열이 되어 정렬·기간 자르기가 전부 어긋납니다.

import pandas as pd

df = pd.read_csv(

    "samsung.csv",

    parse_dates=["date"],   # 문자열이 아니라 날짜 타입으로

    index_col="date",       # 날짜를 인덱스(행 이름)로

)

df = df.sort_index()        # 시간 순서 보장 — 백테스트의 기본 전제

print(df.head())

print(df.dtypes)            # close가 int/float인지 반드시 확인

dtypes 확인은 습관으로 만드세요. 숫자 컬럼에 쉼표(71,000)가 섞여 있으면 pandas는 이걸 문자열로 읽고, 이후 모든 계산이 조용히 이상해집니다.

2. 잘라 보기 — 기간·컬럼 선택

# 특정 기간만 (인덱스가 날짜라서 가능)

recent = df.loc["2026-01-01":"2026-03-31"]

# 종가만

close = df["close"]

# 조건 필터: 거래량이 평소의 2배 넘은 날

spike = df[df["volume"] > df["volume"].mean() * 2]

print(f"거래량 급증일: {len(spike)}일")

여기서 >는 부등호입니다. pandas는 조건식을 주면 True/False 표를 만들고, 그걸 대괄호에 넣으면 True인 행만 남습니다. 이 패턴 하나가 전략 조건 대부분을 커버합니다.

3. 이동평균 — 한 줄이면 끝

이동평균은 "최근 N일 종가의 평균"입니다. 가격의 잔떨림을 줄여 추세를 보기 위해 씁니다.

df["ma5"]  = df["close"].rolling(window=5).mean()

df["ma20"] = df["close"].rolling(window=20).mean()

print(df[["close", "ma5", "ma20"]].tail())

rolling(20)은 앞의 19행에서는 계산할 데이터가 모자라 NaN(빈 값)이 됩니다. 이건 오류가 아니라 정상입니다. 다만 이 구간을 그대로 전략에 넣으면 엉뚱한 신호가 나오므로 잘라내야 합니다.

df = df.dropna()            # 지표가 채워진 구간부터 사용

골든크로스(단기선이 장기선을 위로 뚫는 것) 판정도 같은 방식입니다.

# 어제는 아래, 오늘은 위 → 오늘 교차 발생

cross_up = (df["ma5"] > df["ma20"]) & (df["ma5"].shift(1) <= df["ma20"].shift(1))

print(df[cross_up].index)   # 교차가 일어난 날짜들

shift(1)한 칸 아래로 밀기, 즉 "어제 값"을 뜻합니다. 시계열 계산에서 가장 자주 쓰는 함수이니 이것만은 확실히 익혀 두세요.

4. 수익률 계산

# 일간 수익률 (전일 대비 변화율)

df["ret"] = df["close"].pct_change()

# 누적 수익률 — 1.0이 원금

df["cum"] = (1 + df["ret"]).cumprod()

print(f"기간 수익률: {(df['cum'].iloc[-1] - 1) * 100:.2f}%")

print(f"일변동성(표준편차): {df['ret'].std() * 100:.2f}%")

누적 수익률은 더하는 게 아니라 곱하는 것입니다. +10%, -10%를 더하면 0%처럼 보이지만 실제로는 -1%입니다. 이 차이를 놓치면 백테스트 결과가 실제보다 좋게 나옵니다.

최대 낙폭(고점 대비 얼마나 빠졌는지)도 두 줄이면 나옵니다.

peak = df["cum"].cummax()               # 그 시점까지의 최고점

dd   = df["cum"] / peak - 1             # 고점 대비 하락률

print(f"최대 낙폭(MDD): {dd.min() * 100:.2f}%")

5. 초보가 자주 밟는 지뢰 3개

  • 정렬 안 함 — CSV가 최신순으로 저장돼 있는데 그대로 rolling을 돌리면 미래 데이터로 과거를 계산하게 됩니다. sort_index()는 필수입니다.
  • 미래 참조(look-ahead) — 오늘 종가로 계산한 신호로 오늘 종가에 사는 백테스트는 현실에서 불가능합니다. 신호는 shift(1)로 하루 미뤄 적용하세요.
  • 결측 무시 — 휴장일이나 데이터 누락 구간을 확인하지 않으면 "20일 이동평균"이 실제로는 30일치 기간을 덮을 수 있습니다.

6. 결과 저장

df.to_csv("samsung_with_indicators.csv", encoding="utf-8-sig")

utf-8-sig는 엑셀에서 한글이 깨지지 않게 해 주는 옵션입니다. 이것만 기억해도 확인 작업이 훨씬 편해집니다.

정리

  • parse_dates + index_col + sort_index() — 읽기 3종 세트
  • 지표는 rolling(), 어제 값은 shift(1)
  • 누적 수익률은 곱셈(cumprod), 낙폭은 cummax 대비
  • NaN 구간은 잘라내고, 신호는 하루 미뤄 적용

pandas는 기능이 방대하지만, 자동매매에 실제로 쓰이는 건 이 정도가 8할입니다. 나머지는 필요할 때 찾아 쓰면 됩니다.

댓글

이 블로그의 인기 게시물

한국투자증권 KIS API로 실시간 시세 받기 (WebSocket 실전)

파이썬으로 업비트 API 연동하기 — 시세 조회부터 주문까지 기초

Go로 자동매매 신호봇 프레임워크 설계하기