본문 바로가기
  • plotly로 바로쓰는 동적시각화 in R & 파이썬
금융 데이터 분석

Python으로 배우는 ‘퀀트를 위한 머신러닝’ 시리즈 - Part 1

by 아참형인간 2026. 6. 9.

학습 목표

이 튜토리얼을 마치면 다음을 수행할 수 있습니다.

  • 금융 분야의 관점에서 인공지능(AI), 머신러닝(ML), 딥러닝(DL)의 차이를 설명할 수 있습니다.
  • 지연 수익률(lagged returns) 데이터셋을 생성하여 모멘텀 전략을 위한 피처를 설계할 수 있습니다.
  • scikit-learn을 사용하여 선형 회귀 모델을 학습하고 평가할 수 있습니다.
  • 모델의 복잡성이 활용 가능한 신호(signal)를 초과할 때 발생하는 과적합을 시각적·수학적으로 식별할 수 있습니다.

사전 준비 사항

이 튜토리얼을 따라 하기 위해서는 다음이 필요합니다.

  • Python 3.7 이상
  • Jupyter Notebook 또는 유사한 개발 환경(VS Code, Google Colab 등)
  • 금융 데이터 접근 환경: yfinance를 통해 시장 데이터를 다운로드하기 위한 인터넷 연결

1. ML 계층 구조: AI vs. ML vs. DL

우리가 수행하는 작업이 어디에 속하는지 이해하는 것은 매우 중요합니다.

인공지능(Artificial Intelligence, AI)

지적 작업을 자동화하려는 광범위한 노력 전체를 의미합니다. 규칙 기반 알고리즘(예: “MA50 > MA200이면 매수”)도 AI에 속하지만 머신러닝은 아닙니다.

머신러닝(Machine Learning, ML)

AI의 하위 분야로, 규칙을 명시적으로 프로그래밍하는 대신 데이터로부터 학습하는 방식입니다. 입력값(과거 수익률)과 출력값(미래 수익률)을 제공하면 모델이 그 관계를 스스로 학습합니다.

딥러닝(Deep Learning, DL)

머신러닝의 하위 분야로, 여러 층으로 구성된 신경망(neural network)을 사용하여 데이터의 표현(representation)을 학습합니다.

2. 지도학습(Supervised Learning)과 선형 회귀(Linear Regression)

우리는 지도학습(Supervised Learning) 을 수행합니다. 왜냐하면 레이블(label)이 포함된 데이터셋을 가지고 있기 때문입니다. 즉, 과거 지표(feature)에 대응하는 실제 미래 수익률(label)을 알고 있습니다.

이번 예제에서는 정량적 머신러닝의 “Hello World”라 할 수 있는 선형 회귀(Linear Regression) 를 사용합니다.

선형 회귀는 종속 변수 (y) (미래 수익률)와 독립 변수 (X) (과거 수익률) 간의 관계를 선형 방정식으로 모델링합니다.

\hat{y}=\theta_0+\theta_1x_1+\theta_2x_2+\cdots+\theta_nx_n

여기서

  • (\hat{y})는 예측값입니다.
  • (x_i)는 특징(feature)입니다. (예: 전일 수익률, 2일 전 수익률 등)
  • (\theta)(세타)는 모델이 학습 과정에서 오차를 최소화하기 위해 학습하는 파라미터(가중치)입니다.

3. 과적합(Overfitting)의 함정

금융 데이터에는 많은 잡음(noise)이 존재합니다.

과적합(Overfitting)은 모델이 실제 신호(signal)가 아니라 무작위적인 시장 변동(노이즈)을 학습하는 현상을 의미합니다.

과소적합(Underfitting)

모델이 지나치게 단순한 경우입니다. 예를 들어 곡선 형태의 데이터를 직선 하나로 설명하려고 하는 상황입니다.

과적합(Overfitting)

모델이 지나치게 복잡한 경우입니다. 예를 들어 고차 다항식이 모든 데이터 점을 하나하나 연결하려고 하는 상황입니다. 과거 데이터에서는 완벽해 보이지만, 새로운 미관측 데이터에서는 매우 나쁜 성능을 보입니다.

실습

Step 1: 환경 설정 및 데이터 수집

먼저 필요한 도구를 불러오고 S&P 500을 추종하는 SPY ETF의 과거 데이터를 가져오겠습니다.

import numpy as np
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import PolynomialFeatures

# SPY의 과거 데이터 다운로드
# 충분한 데이터 포인트를 확보하기 위해 긴 기간을 사용합니다.
df = yf.download('SPY', start='2013-01-01', end='2026-01-01')

# 일별 수익률 계산
df['Return'] = df['Close'].pct_change()

# pct_change로 생성된 NaN 값 제거
df.dropna(inplace=True)

print(f"Data Loaded: {len(df)} days of trading data.")
print(df.head())

Step 2: 피처 엔지니어링 (모멘텀 신호)

모멘텀 전략은 과거 성과가 미래 결과에 영향을 준다는 아이디어에 기반합니다. 여기서는 (t-1), (t-2) 등의 시점 수익률을 사용하여 (t) 시점의 수익률을 예측하는 “지연(lagged)” 피처를 생성합니다.

# 지연 피처 생성
# 과거 5일의 수익률을 사용하여 오늘의 수익률을 예측합니다.
lags = 5

for i in range(1, lags + 1):
    df[f'Lag_{i}'] = df['Return'].shift(i)

# shift로 생성된 NaN 값 제거
df.dropna(inplace=True)

# 특징(X)과 타깃(y) 정의
feature_cols = [f'Lag_{i}' for i in range(1, lags + 1)]
X = df[feature_cols]
y = df['Return']

print("Feature Matrix X (First 5 rows):")
print(X.head())

Step 3: 데이터셋 분할

모델을 학습에 사용한 동일한 데이터로 평가해서는 안 됩니다. 시계열 금융 데이터에서는 시간의 순서가 중요하기 때문에 데이터를 무작위로 섞을 수 없습니다. 따라서 시간 순서대로 분할합니다.

# 학습 세트(80%)와 테스트 세트(20%)로 분할
# 시계열 데이터에서는 shuffle=False가 매우 중요합니다.
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, shuffle=False
)

print(f"Training set size: {len(X_train)}")
print(f"Testing set size: {len(X_test)}")

Step 4: 선형 회귀 모델 학습

이제 모델을 생성하고 학습 데이터에 적합시킵니다.

# 모델 초기화
lr_model = LinearRegression()

# 모델 학습
lr_model.fit(X_train, y_train)

# 학습된 계수 확인 ("Theta" 값)
print("Intercept (Bias):", lr_model.intercept_)
print("Coefficients (Weights):", lr_model.coef_)

Step 5: 성능 평가 및 시각화

간단한 모델이 수익률을 얼마나 잘 예측하는지 확인해 보겠습니다.

# 테스트 세트에 대한 예측 수행
y_pred = lr_model.predict(X_test)

# 평균제곱오차(MSE) 계산
mse = mean_squared_error(y_test, y_pred)
print(f"Mean Squared Error (Linear Model): {mse:.8f}")

# 시각화: 실제값 vs 예측값 (보기 쉽게 일부만 표시)
plt.figure(figsize=(12, 6))
plt.plot(y_test.values[:100], label='Actual Returns', alpha=0.7)
plt.plot(y_pred[:100], label='Predicted Returns', alpha=0.7, linestyle='--')
plt.title('Linear Regression: Actual vs Predicted Returns (First 100 Test Days)')
plt.legend()
plt.show()

전체 크기로 이미지를 보려면 Enter를 누르거나 이미지를 클릭하세요.

참고: 실제 변동성에 비해 예측값이 “평평한 선”처럼 보이는 것을 확인할 수 있습니다. 이는 과소적합(Underfitting)입니다. 선형 관계가 너무 약해서 시장의 잡음을 포착하지 못하는 것입니다.

Step 6: 과적합 강제 생성하기 (다항 피처)

과적합을 이해하기 위해 기존 피처의 거듭제곱 형태인 다항 피처를 추가하여 의도적으로 모델을 지나치게 복잡하게 만들어 보겠습니다.

# 다항 피처 생성 (4차 - 매우 복잡한 모델)
poly = PolynomialFeatures(degree=4)

X_train_poly = poly.fit_transform(X_train)
X_test_poly = poly.transform(X_test)

# 복잡한 데이터에 새로운 모델 학습
poly_model = LinearRegression()
poly_model.fit(X_train_poly, y_train)

# 학습 세트와 테스트 세트에 대해 예측
y_train_pred_poly = poly_model.predict(X_train_poly)
y_test_pred_poly = poly_model.predict(X_test_poly)

# 오차 계산
train_mse = mean_squared_error(y_train, y_train_pred_poly)
test_mse = mean_squared_error(y_test, y_test_pred_poly)

print(f"Training MSE (Poly Model): {train_mse:.8f}")
print(f"Testing MSE (Poly Model):  {test_mse:.8f}")

해석: 학습 MSE는 낮지만(좋은 적합), 테스트 MSE는 매우 크게 나타납니다(나쁜 적합). 모델이 학습 데이터를 외워버렸지만 일반화하지 못한 것입니다.

작업 확인

형태 확인

X_train에 5개의 열(Lag_1부터 Lag_5)이 있는지 확인합니다.

날짜 정렬

X_test의 인덱스가 X_train 바로 뒤에 이어지는지 확인합니다.

출력 로직

선형 회귀 계수는 0에 가까운 작은 숫자여야 합니다. 이는 효율적 시장에서 선형 신호가 약하다는 것을 의미합니다.

도전 과제: “윈도우(Window)” 찾기

Step 2의 코드에서 lags 변수를 5에서 20으로 변경해 보십시오.

과거 기간, 즉 피처 수를 늘리면 테스트 MSE가 개선됩니까?

다항 변환을 적용했을 때 과적합이 더 심해집니까?

결론 및 다음 단계

이 튜토리얼에서는 정량적 머신러닝 실험의 전체 파이프라인을 구축했습니다.

Data Loading -> Feature Engineering -> Train/Test Split -> Modeling -> Evaluation

우리는 단순한 선형 회귀가 금융 데이터를 과소적합하는 경향이 있음을 확인했습니다. 즉, 모델이 너무 단순합니다. 반면 고차 다항 회귀는 과적합됩니다. 즉, 잡음을 외워버립니다.

다음 단계

학습할 수 있을 만큼 충분히 복잡하면서도 일반화할 수 있을 만큼 견고한 “골디락스(Goldilocks)” 영역은 어떻게 찾을 수 있을까요?

그 답은 정규화(Regularization)에 있습니다. 다음 글에서는 Ridge, Lasso, ElasticNet 회귀를 다루며, 모델 복잡도에 수학적 패널티를 부여하여 매매 전략을 개선하는 방법을 살펴보겠습니다.

문제 해결 / FAQ

Q: y_pred가 거의 0 근처의 직선처럼 보입니다.

A: 금융 수익률에서는 예상되는 결과입니다. 일별 수익률은 0에 매우 가깝고, 과거 수익률에서 얻는 “신호”도 약합니다. 이는 더 복잡한 피처나 정규화 없이 원시 가격을 단순 선형 관계로 예측하는 것이 충분하지 않은 경우가 많다는 점을 보여줍니다.

Q: yfinance가 데이터를 다운로드하지 못했습니다.

A: 인터넷 연결이 활성화되어 있는지 확인하십시오. 가끔 Yahoo Finance API가 요청을 제한할 수 있습니다. 잠시 기다린 후 다시 시도하거나, SPY 데이터를 CSV로 직접 다운로드한 뒤 pd.read_csv()를 사용해 불러오십시오.

Q: 왜 train_test_split에서 shuffle=False를 사용합니까?

A: 금융 데이터는 순차적입니다. 데이터를 섞으면 2022년 데이터를 사용하여 2015년 데이터를 예측하는 상황이 발생할 수 있습니다. 이는 미래를 알고 예측하는 “전방 참조 편향(look-ahead bias)”에 해당합니다. 따라서 항상 시간 순서를 유지해야 합니다.

댓글