투자할 자산을 비교하여 선택하고자 할 때, 기본으로 살펴보아야 하는 것은 자산 수익률의 특성입니다. 과거 데이터에 국한되긴 하지만, 어느 정도의 수익률을 기대할 수 있었고, 위험은 얼마나 높았는지 살펴보는 것입니다. 이러한 수치를 통계량(statistic)이라 합니다. 평균 수익률, 최소 수익률, 하위 10% 수익률, MDD 모두 통계량입니다.
이 글에서는 자산의 주가 데이터에서 평균, 중앙값, 최소, 최대와 같은 기초 통계량을 추출하는 방법을 알아보고, 확률 분포 그래프를 그려보겠습니다.

주의: 이 글은 특정 상품 또는 특정 전략에 대한 추천의 의도가 없습니다. 이 글에서 제시하는 수치는 과거에 그랬다는 기록이지, 앞으로도 그럴 거라는 예상이 아닙니다. 분석 대상, 기간, 방법에 따라 전혀 다른 결과가 나올 수 있습니다. 데이터 수집, 가공, 해석 단계에서 의도하지 않은 오류가 있을 수 있습니다. 일부 설명은 편의상 현재형으로 기술하지만, 데이터 분석에 대한 설명은 모두 과거형으로 이해해야 합니다.
기초 통계량 살펴보기
DataFrame은 기초적인 통계량을 추출하는 describe()라는 함수를 제공합니다. 다음은 SPY의 1년(252거래일) 수익률에 대한 기초 통계량을 살펴본 결과입니다. 252일은 미국 주식 시장의 1년 평균 거래일수에 가깝습니다.
spy = fdr.DataReader('SPY')['Adj Close']
spy.pct_change(252).describe().round(3)
SPY 주가 데이터 중에서 수정 주가만 골라 spy 변수에 넣었습니다. pct_change(252)로 모든 252거래일 수익률을 찾아 기초 통계량을 추출하라고 지시했습니다. round() 함수로 보기 편하게 소수 세째 자리에서 반올림했습니다. 다음과 같은 결과가 나옵니다.

252거래일로 윈도를 만들면 7,855개가 만들어집니다. 산술 평균 수익률은 11.8%, 표준 편차는 17.0%였습니다. 최소 수익률은 -47.4%였고, 중앙값(50%)은 14.0%였습니다.
수익률의 확률 분포 그래프 그리기
spy.pct_change(252)는 모든 1년 수익률이기에 일종의 배열입니다. 값이 여럿 있으니 확률 분포로 나타낼 수 있습니다. 다음은 확률 분포 그래프를 그리는 코드입니다.
spy_252 = spy.pct_change(252)
spy_252.hist(bins = 50, histtype = 'step', density = True)
plt.xlabel('1 Year (252 days) Return')
plt.ylabel('Probability density')
plt.gca().xaxis.set_major_formatter(PercentFormatter(xmax = 1))
중요한 부분만 살펴보겠습니다. 참고: 이 연재는 순서대로 읽으면서 하나씩 차근차근 직접 해 보기를 권합니다.
spy_252.hist(bins = 50, histtype = 'step', density = True)
hist() 함수로 히스토그램(histogram; 도수분포 그래플)을 그립니다. 구간의 수(bins)는 50개로 지정하고, 외곽선만 표현하도록(histtype = 'step') 설정했습니다. 기본으로 x축은 횟수입니다. density = True로 설정하면 확률 밀도로 표현됩니다. 확률 밀도를 사용해야 총빈도가 다른 두 데이터의 확률 분포를 비교할 수 있습니다.
그 아래에 x축과 y축 이름을 지정하고, x축 수치를 퍼센트(%)로 표현하기 위해 PercentFormatter를 사용했습니다.
결과는 다음과 같습니다.

그럴듯한 결과가 나왔습니다. 한눈에 봐도 정규 분포(normal distribution)와는 조금 달라 보입니다. 함께 그려서 비교해 보겠습니다. 수치적 투자 분석에서는 모델링을 단순화하기 위해 정규 분포(또는 로그 정규 분포)를 가정하는 경우가 꽤 많습니다. 현실과는 다소 차이가 날 수 있으니 분석 시 항상 염두에 두어야 합니다.
다음은 정규 분포를 그래프에 함께 나타내는 코드입니다.
from scipy.stats import norm
spy_252.hist(bins = 50, histtype = 'step', density = True, label = 'SPY')
spy_mean = spy_252.mean()
spy_std = spy_252.std()
xs = np.arange(spy_252.min(), spy_252.max(), 0.01)
ys = [norm.pdf(x, loc = spy_mean, scale = spy_std) for x in xs]
plt.plot(xs, ys, label = f'$N({spy_mean * 100:.1f}\%, {spy_std * 100:.1f}\%^2)$')
plt.xlabel('1 Year (252 days) Return')
plt.ylabel('Probability density')
plt.gca().xaxis.set_major_formatter(PercentFormatter(xmax = 1))
plt.legend()
추가된 주요 사항을 살펴봅니다.
from scipy.stats import norm
scipy(싸이파이라고 읽습니다)는 통계 및 기계 학습(machine learning)과 관련한 다양한 기능을 제공하는 모듈입니다. 그중에서 정규 분포 함수를 사용하겠다고 지시했습니다.
spy_mean = spy_252.mean()
spy_std = spy_252.std()
1년 수익률의 평균과 표준 편차를 구합니다.
xs = np.arange(spy_252.min(), spy_252.max(), 0.01)
ys = [norm.pdf(x, loc = spy_mean, scale = spy_std) for x in xs]
np.arange() 함수로 x값의 범위를 SPY 1년 수익률과 최소값부터 최대값까지 1% 단위로 배열을 만듭니다. 이어 정규 분포의 PDF(Probability Density Function; 확률 밀도 함수)를 이용하여 각각의 x값에 해당되는 확률 밀도를 계산해서 ys에 넣습니다. norm.pdf()에서 loc가 평균이고, scale이 표준 편차입니다.
plt.plot(xs, ys, label = f'$N({spy_mean * 100:.1f}\%, {spy_std * 100:.1f}\%^2)$')
x값과 y값 목록이 만들어졌으니 이를 이용하여 그래프를 그립니다. label은 범례에 표시할 이름을 지정하는 것입니다.
f-string 포맷터를 사용했고, 문자열의 앞 뒤에 달러($)가 있습니다. 이는 그 안에 있는 문자를 수식 표현에 편리한 LaTeX 형식으로 해석하라는 의미입니다. 예를 들어 2^3 = 2³으로 표시됩니다.
퍼센트(%) 기호는 f-string 포맷터에서 특별한 의미를 가지므로 그대로 사용하기 위해 이스케이프(escape) 문자인 백슬래쉬(\)를 앞에 붙였습니다. {spy_mean * 100:.1f}는 spy_mean에 100을 곱한 후에 소수점 1자리인 실수(float)로 표시하라는 의미입니다.
실행하면, 다음과 같이 SPY의 1년 수익률 분포가 이를 모사(approximation)한 정규 분포와 함께 그려집니다.

비슷한 부분도 있지만, 꽤 달라 보이는 부분도 있습니다. 평균 수익률 근처의 분포가 정규 분포보다 밀집되어 있으며, 큰 손실률이 발생한 빈도도 높았습니다.
수익률 분포는 손익 비대칭성이 있기에, 이처럼 좌우 대칭을 가정한 정규 분포로 모델링하면 충분히 정확하지 않을 수 있습니다. 하지만 정규 분포는 수익률 분포와 그나마 흡사하며, 각종 연산 및 추론에 편리하기에 자주 사용합니다. 조금 더 현실적으로 모델링하고자 한다면, 정규 분포보다는 로그 정규 분포(log-normal distribution)가 보다 적합할 수 있습니다.
정리하며
SPY의 1년 수익률의 기초 통계량을 추출하고, 확률 분포 그래프도 그려보았습니다. 대응하는 정규 분포와 비교해 보면 비슷한 점도 있지만, 꽤 다른 점도 발견할 수 있었습니다.
이어지는 글에서는 환율을 고려했을 때 확률 분포가 어떻게 변하는지 살펴봅니다.
참고: 연재와 관련한 질문은 댓글로 남겨주시기 바랍니다. 답변을 드리거나 이후 연재에서 다룰 수 있도록 노력하겠습니다.
참고 서적: 왜 위험한 주식에 투자하라는 걸까? - 장기 투자와 분산 투자에 대한 통계학적 시각
이어지는 글: [파이썬 분석 5] 환율을 적용해 보고 어떤 변화가 발생했는지 살펴보자
연재 목록: 자산 배분 분석 방법 책 소개, 연재글 및 사례 모음 [목록]
함께 읽으면 좋은 글 (최신 글)
- [파이썬 분석 3] 누적 수익률로 그래프로 그려 보자 (로그 스케일에 수익률을 표현하는 방법)
- [파이썬 분석 2] PR(배당 미고려)과 TR(배당 재투자) 주가 흐름을 그래프로 그려 보자 (FinanceDataReader 모듈 사용)
- [파이썬 분석 1] 주가 흐름을 그래프로 그려 보자 (구글 코랩을 써 보자! 인공지능 너도 실수하는구나?)
- [중급 14] 레버리지 ETF의 성과는 왜 좋았을까? (민감한 레버리지님과 기준 금리)
- [중급 13] 레버리지 배율은 무한정 높여도 될까? (복리 수익률을 평균-분산 그래프에 나타내 보자)
함께 읽으면 좋은 글 (인기 글)
'주식투자' 카테고리의 다른 글
[파이썬 분석 8] 세 가지 자산에 분산 투자한 결과를 살펴보자 (+재사용을 위한 함수 정의) (2) | 2025.04.14 |
---|---|
[파이썬 분석 7] 두 가지 자산을 혼합해 보고, 수익률 분포의 변화를 살펴보자 (0) | 2025.04.14 |
[파이썬 분석 6] 산점도(scatter plot)에 자산의 특성을 나타내고, 예금과 혼합 효과도 표현해 보자 (0) | 2025.04.13 |
[파이썬 분석 5] 환율을 적용해 보고 어떤 변화가 발생했는지 살펴보자 (0) | 2025.04.13 |
[파이썬 분석 3] 누적 수익률로 그래프로 그려 보자 (로그 스케일에 수익률을 표현하는 방법) (0) | 2025.04.12 |
[파이썬 분석 2] PR(배당 미고려)과 TR(배당 재투자) 주가 흐름을 그래프로 그려 보자 (FinanceDataReader 모듈 사용) (0) | 2025.04.12 |
[파이썬 분석 1] 주가 흐름을 그래프로 그려 보자 (구글 코랩을 써 보자! 인공지능 너도 실수하는구나?) (1) | 2025.04.11 |
[중급 14] 레버리지 ETF의 성과는 왜 좋았을까? (민감한 레버리지님과 기준 금리) (0) | 2025.04.11 |