Skip to main content

pandas로 주식 데이터 분석하기

Views 97약 7분생성형 AI 활용
Summary

주가 데이터는 그대로는 말해주지 않는다

주가 데이터를 가져오면 처음에는 표가 보입니다. 날짜, 시가, 고가, 저가, 종가, 거래량 같은 열이 있습니다. 하지만 표만 보고는 투자 판단을 하기 어렵습니다.

투자자가 알고 싶은 것은 단순한 숫자 목록이 아닙니다.

  • 이 종목은 얼마나 올랐는가?

  • 오르는 동안 얼마나 흔들렸는가?

  • 시장보다 강했는가, 약했는가?

  • 다른 종목과 비슷하게 움직였는가?

  • 최근 추세는 좋아지고 있는가?

이 질문에 답하려면 원본 데이터를 분석용 데이터로 바꿔야 합니다. 이때 가장 많이 쓰는 도구가 pandas입니다. pandas는 표 형태의 데이터를 다루는 파이썬 라이브러리입니다. 투자 데이터는 대부분 표입니다. 그래서 pandas를 익히면 주식 데이터를 훨씬 쉽게 다룰 수 있습니다.

이번 장에서는 복잡한 전략을 만들지 않습니다. 목표는 하나입니다. 가져온 주가 데이터를 투자자가 읽을 수 있는 정보로 바꾸는 것입니다.

pandas로 주식 데이터 분석하기AI 생성 이미지

종가 그래프부터 그려보기

먼저 한 종목의 종가 흐름을 확인해보겠습니다. 이전 장에서 yfinance로 데이터를 가져왔다면 대략 다음과 같은 형태일 것입니다.

import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt

df = yf.download("AAPL", start="2020-01-01", auto_adjust=True)
print(df.head())

auto_adjust=True를 사용하면 배당과 액면분할 등이 반영된 가격을 기준으로 데이터를 받을 수 있습니다. 이 경우 보통 Close 열을 분석 기준 가격으로 사용해도 됩니다.

종가 그래프는 다음처럼 그립니다.

plt.figure(figsize=(12, 5))
plt.plot(df.index, df["Close"], label="AAPL Close")
plt.title("AAPL Price")
plt.xlabel("Date")
plt.ylabel("Price")
plt.legend()
plt.grid(True)
plt.show()

종가 그래프는 직관적입니다. 가격이 오른 구간과 내린 구간이 눈에 보입니다. 하지만 종가 그래프만으로 성과를 비교하기는 어렵습니다.

예를 들어 10달러에서 20달러가 된 종목과 500달러에서 550달러가 된 종목을 생각해봅시다. 가격 상승폭만 보면 두 번째 종목이 50달러 올랐습니다. 하지만 수익률은 첫 번째 종목이 100%, 두 번째 종목은 10%입니다.

투자 성과를 볼 때 중요한 것은 가격의 절대 수준이 아니라 수익률입니다.

일간 수익률 계산하기

수익률은 이전 가격 대비 현재 가격이 얼마나 변했는지를 나타냅니다. pandas에서는 pct_change()를 사용하면 쉽게 계산할 수 있습니다.

df["daily_return"] = df["Close"].pct_change()
print(df[["Close", "daily_return"]].head())

첫 번째 날짜의 수익률은 계산할 수 없습니다. 전날 가격이 없기 때문입니다. 그래서 첫 번째 값은 NaN으로 표시됩니다. NaN은 결측값, 즉 비어 있는 값이라는 뜻입니다.

필요하다면 다음처럼 제거할 수 있습니다.

df = df.dropna()

일간 수익률 그래프도 그려보겠습니다.

plt.figure(figsize=(12, 4))
plt.plot(df.index, df["daily_return"], label="Daily Return")
plt.axhline(0, color="black", linewidth=1)
plt.title("AAPL Daily Return")
plt.xlabel("Date")
plt.ylabel("Return")
plt.legend()
plt.grid(True)
plt.show()

수익률 그래프는 종가 그래프와 다르게 보입니다. 가격 그래프는 추세를 보여줍니다. 수익률 그래프는 하루하루 얼마나 흔들렸는지를 보여줍니다.

장기적으로 상승한 종목도 중간에는 큰 하락일이 많을 수 있습니다. 반대로 가격은 별로 오르지 않았지만 변동이 매우 큰 종목도 있습니다. 그래서 투자자는 가격 그래프와 수익률 그래프를 함께 봐야 합니다.

누적 수익률로 전체 성과 보기

일간 수익률은 하루 단위의 변화입니다. 하지만 투자자는 일정 기간 동안 전체적으로 얼마를 벌었는지도 알고 싶습니다. 이때 누적 수익률을 계산합니다.

누적 수익률은 매일의 수익률을 계속 곱해서 계산합니다.

df["cum_return"] = (1 + df["daily_return"]).cumprod() - 1

여기서 cumprod()는 누적 곱을 의미합니다. 예를 들어 첫날 2% 오르고 다음 날 3% 오르면 단순히 5%가 아닙니다. 돈은 복리로 움직이기 때문에 1.02 × 1.03처럼 곱해집니다.

누적 수익률을 그래프로 그려보겠습니다.

plt.figure(figsize=(12, 5))
plt.plot(df.index, df["cum_return"], label="Cumulative Return")
plt.axhline(0, color="black", linewidth=1)
plt.title("AAPL Cumulative Return")
plt.xlabel("Date")
plt.ylabel("Cumulative Return")
plt.legend()
plt.grid(True)
plt.show()

누적 수익률 그래프는 투자 성과를 가장 직접적으로 보여줍니다. 0보다 위에 있으면 시작 시점 대비 수익 상태이고, 0보다 아래에 있으면 손실 상태입니다.

다만 시작 날짜에 따라 결과가 크게 달라질 수 있습니다. 2020년 초부터 본 성과와 2022년 고점부터 본 성과는 다를 수 있습니다. 그래서 분석할 때는 항상 기간을 함께 적어야 합니다.

이동평균선 만들기

이동평균선은 일정 기간의 평균 가격을 선으로 나타낸 것입니다. 예를 들어 20일 이동평균선은 최근 20거래일 종가의 평균입니다.

pandas에서는 rolling()과 mean()을 함께 사용합니다.

df["ma20"] = df["Close"].rolling(window=20).mean()
df["ma60"] = df["Close"].rolling(window=60).mean()

그래프로 확인해보겠습니다.

plt.figure(figsize=(12, 5))
plt.plot(df.index, df["Close"], label="Close", alpha=0.6)
plt.plot(df.index, df["ma20"], label="MA20")
plt.plot(df.index, df["ma60"], label="MA60")
plt.title("AAPL Moving Averages")
plt.xlabel("Date")
plt.ylabel("Price")
plt.legend()
plt.grid(True)
plt.show()

이동평균선은 가격의 노이즈를 줄여줍니다. 하루하루의 작은 변동보다 큰 흐름을 보기 쉽게 만듭니다.

일반적으로 짧은 이동평균선은 최근 가격 변화에 민감하게 반응합니다. 긴 이동평균선은 느리게 움직이지만 장기 흐름을 보여줍니다. 예를 들어 20일선은 단기 흐름, 60일선은 중기 흐름을 보는 데 자주 사용됩니다.

하지만 이동평균선 자체가 답은 아닙니다. "20일선이 60일선을 돌파하면 무조건 산다"는 식의 말은 검증 전까지는 가설일 뿐입니다. 다음 장들에서 이런 규칙을 실제 전략으로 만들고 백테스트해볼 것입니다.

이번 장에서는 이동평균선이 어떻게 계산되는지, 그리고 그래프에서 무엇을 보여주는지 이해하는 것으로 충분합니다.

거래량 변화도 함께 보기

가격만 보면 시장의 관심 변화를 놓칠 수 있습니다. 거래량은 해당 기간에 얼마나 많이 거래되었는지를 보여줍니다. 가격 상승과 함께 거래량이 늘어나는지, 가격 하락 중 거래량이 커지는지도 참고할 수 있습니다.

거래량 이동평균을 만들어보겠습니다.

df["volume_ma20"] = df["Volume"].rolling(window=20).mean()
plt.figure(figsize=(12, 4))
plt.bar(df.index, df["Volume"], label="Volume", alpha=0.4)
plt.plot(df.index, df["volume_ma20"], label="Volume MA20", color="red")
plt.title("AAPL Volume")
plt.xlabel("Date")
plt.ylabel("Volume")
plt.legend()
plt.grid(True)
plt.show()

거래량은 단독으로 해석하면 위험합니다. 거래량이 늘었다고 반드시 좋은 신호는 아닙니다. 급락하면서 거래량이 폭증할 수도 있기 때문입니다. 그래서 가격, 수익률, 추세와 함께 봐야 합니다.

여러 종목을 같은 기준으로 비교하기

주식 가격은 종목마다 다릅니다. 어떤 종목은 50달러이고, 어떤 종목은 500달러입니다. 그래서 가격 그래프를 그대로 겹쳐 그리면 비교가 어렵습니다.

여러 종목을 비교할 때는 수익률 기준으로 바꾸는 것이 좋습니다.

tickers = ["AAPL", "MSFT", "NVDA", "SPY"]
prices = yf.download(tickers, start="2020-01-01", auto_adjust=True)["Close"]
print(prices.head())

prices는 각 종목의 종가가 열로 들어 있는 데이터프레임입니다.

먼저 일간 수익률을 계산합니다.

returns = prices.pct_change().dropna()

누적 수익률도 계산합니다.

cum_returns = (1 + returns).cumprod() - 1

이제 여러 종목의 누적 수익률을 같은 그래프에 그릴 수 있습니다.

plt.figure(figsize=(12, 5))
for col in cum_returns.columns:
    plt.plot(cum_returns.index, cum_returns[col], label=col)

plt.axhline(0, color="black", linewidth=1)
plt.title("Cumulative Returns")
plt.xlabel("Date")
plt.ylabel("Cumulative Return")
plt.legend()
plt.grid(True)
plt.show()

이 그래프는 시작 시점에 1원을 투자했다고 가정했을 때 각 종목이 얼마나 성장했는지를 보여줍니다. 가격 수준이 달라도 같은 기준으로 비교할 수 있습니다.

또 다른 방법은 가격을 100으로 표준화하는 것입니다.

normalized = prices / prices.iloc[0] * 100
normalized.plot(figsize=(12, 5), grid=True)
plt.title("Normalized Price: Start = 100")
plt.ylabel("Index")
plt.show()

시작점을 100으로 맞추면 각 종목의 상대적 흐름을 쉽게 볼 수 있습니다. 150이면 시작 시점보다 50% 상승했다는 뜻이고, 80이면 20% 하락했다는 뜻입니다.

시장지수와 비교하기

개별 종목의 성과는 시장과 비교해야 의미가 생깁니다. 어떤 종목이 20% 올랐다고 해도 같은 기간 시장지수가 40% 올랐다면 상대적으로는 약한 성과입니다. 반대로 시장이 크게 하락한 기간에 손실을 작게 방어했다면 좋은 특징일 수 있습니다.

여기서는 SPY를 미국 주식 시장의 대표 지수 ETF로 사용해 비교해보겠습니다.

stock = "AAPL"
benchmark = "SPY"

data = yf.download([stock, benchmark], start="2020-01-01", auto_adjust=True)["Close"]
ret = data.pct_change().dropna()
cum = (1 + ret).cumprod() - 1
cum.plot(figsize=(12, 5), grid=True)
plt.title("AAPL vs SPY")
plt.ylabel("Cumulative Return")
plt.show()

이렇게 보면 개별 종목이 시장보다 강했던 구간과 약했던 구간을 확인할 수 있습니다. 투자자는 "올랐다"에서 멈추면 안 됩니다. "무엇과 비교해서 올랐는가?"를 물어야 합니다.

이 질문은 이후 포트폴리오와 백테스트에서 매우 중요해집니다. 전략의 성과도 마찬가지입니다. 단순히 수익이 났다고 좋은 전략이 아닙니다. 같은 기간 시장을 보유했을 때보다 나았는지 비교해야 합니다.

상관관계 분석하기

여러 종목을 함께 보유할 때 중요한 개념이 상관관계입니다. 상관관계는 두 자산이 얼마나 비슷하게 움직이는지를 나타냅니다.

  • 1에 가까우면 거의 같은 방향으로 움직입니다.

  • 0에 가까우면 뚜렷한 관계가 약합니다.

  • -1에 가까우면 반대 방향으로 움직입니다.

pandas에서는 corr()로 쉽게 계산할 수 있습니다.

corr = returns.corr()
print(corr)

상관관계는 포트폴리오를 만들 때 중요합니다. 서로 다른 종목을 여러 개 샀다고 해서 항상 분산투자가 되는 것은 아닙니다. 모두 같은 업종이고 같은 방향으로 움직인다면 실제 위험은 크게 줄지 않을 수 있습니다.

상관관계를 보기 쉽게 시각화할 수도 있습니다.

plt.figure(figsize=(6, 5))
plt.imshow(corr, cmap="coolwarm", vmin=-1, vmax=1)
plt.colorbar(label="Correlation")
plt.xticks(range(len(corr.columns)), corr.columns, rotation=45)
plt.yticks(range(len(corr.columns)), corr.columns)
plt.title("Return Correlation")
plt.show()

상관관계는 가격이 아니라 수익률로 계산하는 것이 일반적입니다. 가격은 대부분 장기적으로 추세를 가질 수 있어 관계가 과장되어 보일 수 있습니다. 반면 수익률은 같은 기간의 변화율을 비교하므로 두 자산이 실제로 함께 움직였는지를 보기 더 적합합니다.

나만의 종목 분석 노트 만들기

이제 한 종목을 분석할 때 반복해서 사용할 기본 틀을 만들 수 있습니다.

def analyze_stock(ticker, start="2020-01-01"):
    df = yf.download(ticker, start=start, auto_adjust=True)
    df["daily_return"] = df["Close"].pct_change()
    df["cum_return"] = (1 + df["daily_return"]).cumprod() - 1
    df["ma20"] = df["Close"].rolling(20).mean()
    df["ma60"] = df["Close"].rolling(60).mean()

    total_return = df["cum_return"].dropna().iloc[-1]
    volatility = df["daily_return"].std() * (252 ** 0.5)

    print(f"종목: {ticker}")
    print(f"기간 누적 수익률: {total_return:.2%}")
    print(f"연율화 변동성: {volatility:.2%}")

    plt.figure(figsize=(12, 5))
    plt.plot(df.index, df["Close"], label="Close", alpha=0.6)
    plt.plot(df.index, df["ma20"], label="MA20")
    plt.plot(df.index, df["ma60"], label="MA60")
    plt.title(f"{ticker} Price and Moving Averages")
    plt.legend()
    plt.grid(True)
    plt.show()

    return df

사용법은 간단합니다.

apple = analyze_stock("AAPL")

이 함수는 아직 완성된 분석 도구가 아닙니다. 하지만 중요한 출발점입니다. 종목을 볼 때마다 같은 기준으로 가격, 수익률, 추세, 변동성을 확인할 수 있습니다.

투자 판단에서 일관성은 매우 중요합니다. 어떤 종목은 뉴스만 보고, 어떤 종목은 차트만 보고, 어떤 종목은 재무지표만 보면 판단이 흔들립니다. 분석 노트는 투자 질문을 일정한 형식으로 정리하게 도와줍니다.

pandas 분석에서 조심할 점

pandas로 계산하는 것은 쉽습니다. 하지만 계산이 쉽다고 해석까지 쉬운 것은 아닙니다.

첫째, 기간을 확인해야 합니다. 같은 종목도 분석 시작일과 종료일에 따라 수익률이 달라집니다.

둘째, 결측값을 확인해야 합니다. 특정 날짜의 데이터가 비어 있거나 종목마다 거래일이 다르면 비교 결과가 달라질 수 있습니다.

셋째, 수정주가 기준을 확인해야 합니다. 배당과 액면분할을 반영하지 않으면 장기 수익률이 왜곡될 수 있습니다.

넷째, 그래프를 보고 성급히 결론 내리면 안 됩니다. 이동평균선, 거래량, 상관관계는 힌트입니다. 투자 규칙이 되려면 백테스트가 필요합니다.

이번 장에서 한 작업은 모두 다음 단계의 재료가 됩니다. 일간 수익률은 성과 평가의 기초가 되고, 누적 수익률은 전략 결과를 보여주는 곡선이 됩니다. 이동평균선은 첫 번째 매매 전략의 신호가 되고, 상관관계는 포트폴리오 위험을 이해하는 출발점이 됩니다.

주식 데이터 분석의 핵심은 복잡한 기술이 아닙니다. 좋은 질문을 표에 던지고, 계산 가능한 형태로 답을 찾는 것입니다. 이제 우리는 주가 데이터를 단순한 숫자에서 투자 실험의 재료로 바꾸는 첫 단계를 마쳤습니다.

책 목록

처음 만나는 AI 퀀트 투자: 파이썬과 AI로 투자 아이디어를 검증하는 법

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기