[Python 100일 챌린지] Day 98 - 종합 프로젝트 1: 주식 예측 시스템
드디어 실전 프로젝트! 지금까지 배운 모든 것을 총동원해 주식 가격 예측 시스템을 만듭니다. 데이터 수집, 전처리, 모델 학습, 시각화까지 완벽한 머신러닝 파이프라인을 구축해봅시다!
(40분 완독 ⭐⭐⭐⭐)
🎯 프로젝트 목표
📚 사용할 기술 스택
graph LR
A[yfinance<br/>주식 데이터] --> B[Pandas<br/>전처리]
B --> C[scikit-learn<br/>모델 학습]
C --> D[Matplotlib<br/>시각화]
D --> E[완성!]
프로젝트 개요
무엇을 만들까?
삼성전자 주가를 예측하는 시스템
- 과거 주가 데이터 다운로드
- 이동 평균, 변동성 등 특징 생성
- 선형 회귀로 다음 날 가격 예측
- 실제 vs 예측 그래프 시각화
필요한 라이브러리 설치
1
pip install yfinance pandas scikit-learn matplotlib
💡 “종합 프로젝트라니 어려울 것 같아요…“ 걱정 마세요! 지금까지 배운 것들을 하나씩 조합하는 거예요. 차근차근 따라하다 보면 어느새 완성됩니다! 에러 나도 정상입니다. FAQ에 모든 해결법이 있어요! 😊
1단계: 데이터 수집
yfinance로 주식 데이터 가져오기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import yfinance as yf
import pandas as pd
# 삼성전자 주가 다운로드 (최근 1년)
ticker = "005930.KS" # 삼성전자 코드
stock = yf.Ticker(ticker)
# 데이터 다운로드
df = stock.history(period="1y")
print(f"데이터 크기: {df.shape}")
print("\n처음 5개 데이터:")
print(df.head())
# 저장
df.to_csv('samsung_stock.csv')
print("\n데이터 저장: samsung_stock.csv")
출력 예시:
1
2
3
4
5
6
7
8
데이터 크기: (252, 7)
처음 5개 데이터:
Open High Low Close Volume Dividends Stock Splits
Date
2024-06-07 77000.0 77500.0 76500.0 77200.0 12345678 0.0 0.0
2024-06-08 77300.0 78000.0 77100.0 77800.0 15432109 0.0 0.0
...
데이터 구조 파악
1
2
3
4
5
6
7
8
9
10
# 기본 정보
print(df.info())
# 통계 정보
print("\n기본 통계:")
print(df.describe())
# 결측치 확인
print("\n결측치:")
print(df.isnull().sum())
2단계: 데이터 전처리
특징 생성 (Feature Engineering)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import pandas as pd
# 데이터 로드
df = pd.read_csv('samsung_stock.csv', index_col='Date', parse_dates=True)
# 1. 이동 평균 (Moving Average)
df['MA5'] = df['Close'].rolling(window=5).mean() # 5일 이동평균
df['MA20'] = df['Close'].rolling(window=20).mean() # 20일 이동평균
# 2. 변동성 (Volatility)
df['Volatility'] = df['Close'].rolling(window=10).std()
# 3. 가격 변화율 (Price Change Rate)
df['Change'] = df['Close'].pct_change()
# 4. 거래량 변화
df['Volume_Change'] = df['Volume'].pct_change()
# 5. 목표 변수: 다음 날 종가
df['Target'] = df['Close'].shift(-1)
print("특징 생성 후:")
print(df.head(25))
# 결측치 제거 (이동평균 계산으로 생긴 NaN)
df = df.dropna()
print(f"\n결측치 제거 후 데이터 크기: {df.shape}")
데이터 분리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from sklearn.model_selection import train_test_split
# 특징과 레이블 분리
features = ['Open', 'High', 'Low', 'Volume', 'MA5', 'MA20', 'Volatility', 'Change', 'Volume_Change']
X = df[features]
y = df['Target']
# 훈련/테스트 분리 (80:20)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, shuffle=False # 시계열이므로 shuffle=False!
)
print(f"훈련 데이터: {X_train.shape}")
print(f"테스트 데이터: {X_test.shape}")
3단계: 모델 학습
선형 회귀 모델
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
import numpy as np
# 스케일링
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# 모델 학습
model = LinearRegression()
model.fit(X_train_scaled, y_train)
# 예측
y_pred_train = model.predict(X_train_scaled)
y_pred_test = model.predict(X_test_scaled)
# 평가
train_mae = mean_absolute_error(y_train, y_pred_train)
test_mae = mean_absolute_error(y_test, y_pred_test)
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
test_r2 = r2_score(y_test, y_pred_test)
print("=" * 50)
print("모델 평가 결과")
print("=" * 50)
print(f"훈련 MAE: {train_mae:,.0f}원")
print(f"테스트 MAE: {test_mae:,.0f}원")
print(f"테스트 RMSE: {test_rmse:,.0f}원")
print(f"테스트 R²: {test_r2:.4f}")
출력 예시:
1
2
3
4
5
6
7
==================================================
모델 평가 결과
==================================================
훈련 MAE: 458원
테스트 MAE: 512원
테스트 RMSE: 687원
테스트 R²: 0.9823
여러 모델 비교
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from sklearn.ensemble import RandomForestRegressor
from sklearn.tree import DecisionTreeRegressor
models = {
'Linear Regression': LinearRegression(),
'Decision Tree': DecisionTreeRegressor(random_state=42),
'Random Forest': RandomForestRegressor(n_estimators=100, random_state=42)
}
results = {}
for name, model in models.items():
# 학습
model.fit(X_train_scaled, y_train)
# 예측
y_pred = model.predict(X_test_scaled)
# 평가
mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
r2 = r2_score(y_test, y_pred)
results[name] = {'MAE': mae, 'RMSE': rmse, 'R2': r2}
# 결과 출력
print("\n모델 비교:")
print("-" * 70)
print(f"{'Model':<20} {'MAE':>15} {'RMSE':>15} {'R²':>15}")
print("-" * 70)
for name, metrics in results.items():
print(f"{name:<20} {metrics['MAE']:>15,.0f} {metrics['RMSE']:>15,.0f} {metrics['R2']:>15.4f}")
4단계: 결과 시각화
실제 vs 예측 비교
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import matplotlib.pyplot as plt
# 한글 폰트 설정 (맑은 고딕)
plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False
# 그래프 그리기
plt.figure(figsize=(14, 7))
# 테스트 데이터의 인덱스
test_dates = y_test.index
plt.plot(test_dates, y_test, label='실제 주가', linewidth=2, marker='o')
plt.plot(test_dates, y_pred_test, label='예측 주가', linewidth=2, marker='x', linestyle='--')
plt.xlabel('날짜', fontsize=12)
plt.ylabel('주가 (원)', fontsize=12)
plt.title('삼성전자 주가 예측: 실제 vs 예측', fontsize=14)
plt.legend(fontsize=12)
plt.grid(True, alpha=0.3)
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig('stock_prediction.png', dpi=150, bbox_inches='tight')
print("그래프 저장: stock_prediction.png")
plt.show()
오차 분석
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 오차 계산
errors = y_test - y_pred_test
plt.figure(figsize=(14, 5))
# 1. 오차 분포
plt.subplot(1, 2, 1)
plt.hist(errors, bins=30, edgecolor='black')
plt.xlabel('예측 오차 (원)')
plt.ylabel('빈도')
plt.title('예측 오차 분포')
plt.grid(True, alpha=0.3)
# 2. 시간에 따른 오차
plt.subplot(1, 2, 2)
plt.plot(test_dates, errors, marker='o')
plt.axhline(y=0, color='r', linestyle='--')
plt.xlabel('날짜')
plt.ylabel('오차 (원)')
plt.title('시간에 따른 예측 오차')
plt.xticks(rotation=45)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('error_analysis.png', dpi=150, bbox_inches='tight')
print("오차 분석 저장: error_analysis.png")
plt.show()
💻 완성된 전체 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
"""
주식 가격 예측 시스템
- 삼성전자 주가 예측
- 선형 회귀 모델 사용
"""
import yfinance as yf
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
import matplotlib.pyplot as plt
# 한글 폰트 설정
plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False
print("=" * 50)
print("주식 가격 예측 시스템")
print("=" * 50)
# 1. 데이터 수집
print("\n1. 데이터 수집 중...")
ticker = "005930.KS"
stock = yf.Ticker(ticker)
df = stock.history(period="1y")
print(f"✓ 데이터 수집 완료: {len(df)}개 행")
# 2. 특징 생성
print("\n2. 특징 생성 중...")
df['MA5'] = df['Close'].rolling(window=5).mean()
df['MA20'] = df['Close'].rolling(window=20).mean()
df['Volatility'] = df['Close'].rolling(window=10).std()
df['Change'] = df['Close'].pct_change()
df['Volume_Change'] = df['Volume'].pct_change()
df['Target'] = df['Close'].shift(-1)
df = df.dropna()
print(f"✓ 특징 생성 완료: {df.shape[1]}개 컬럼")
# 3. 데이터 분리
print("\n3. 데이터 분리 중...")
features = ['Open', 'High', 'Low', 'Volume', 'MA5', 'MA20', 'Volatility', 'Change', 'Volume_Change']
X = df[features]
y = df['Target']
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, shuffle=False
)
print(f"✓ 훈련: {len(X_train)}개, 테스트: {len(X_test)}개")
# 4. 모델 학습
print("\n4. 모델 학습 중...")
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
model = LinearRegression()
model.fit(X_train_scaled, y_train)
print("✓ 모델 학습 완료")
# 5. 예측 및 평가
print("\n5. 예측 및 평가 중...")
y_pred_test = model.predict(X_test_scaled)
test_mae = mean_absolute_error(y_test, y_pred_test)
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
test_r2 = r2_score(y_test, y_pred_test)
print("\n" + "=" * 50)
print("평가 결과")
print("=" * 50)
print(f"MAE: {test_mae:,.0f}원")
print(f"RMSE: {test_rmse:,.0f}원")
print(f"R²: {test_r2:.4f}")
# 6. 시각화
print("\n6. 시각화 중...")
plt.figure(figsize=(14, 7))
plt.plot(y_test.index, y_test, label='실제 주가', linewidth=2)
plt.plot(y_test.index, y_pred_test, label='예측 주가', linewidth=2, linestyle='--')
plt.xlabel('날짜', fontsize=12)
plt.ylabel('주가 (원)', fontsize=12)
plt.title('삼성전자 주가 예측 결과', fontsize=14)
plt.legend(fontsize=12)
plt.grid(True, alpha=0.3)
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig('stock_prediction_final.png', dpi=150, bbox_inches='tight')
print("✓ 그래프 저장: stock_prediction_final.png")
print("\n" + "=" * 50)
print("프로젝트 완료!")
print("=" * 50)
📊 개선 아이디어
1. 더 많은 특징 추가
1
2
3
4
5
6
7
8
9
10
# RSI (Relative Strength Index)
def calculate_rsi(data, period=14):
delta = data.diff()
gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
return rsi
df['RSI'] = calculate_rsi(df['Close'])
2. 딥러닝 모델 (LSTM)
1
2
3
4
# 추후 고급 과정에서
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense
# ...
3. 여러 종목 비교
1
2
3
4
tickers = ['005930.KS', '000660.KS', '035420.KS'] # 삼성전자, SK하이닉스, NAVER
for ticker in tickers:
# 각 종목별 예측
pass
⚠️ 주의사항
1. 과거 데이터로 미래를 예측할 수 없습니다
이 프로젝트는 학습용입니다. 실제 투자에 사용하지 마세요!
2. 시계열 데이터는 shuffle하지 않습니다
1
2
3
4
5
# ❌ 잘못된 방법
X_train, X_test, y_train, y_test = train_test_split(X, y, shuffle=True)
# ✅ 올바른 방법
X_train, X_test, y_train, y_test = train_test_split(X, y, shuffle=False)
3. 데이터 누수 방지
1
2
# Target을 만들 때 미래 데이터 사용 금지
df['Target'] = df['Close'].shift(-1) # 다음 날 가격
📝 요약
- 데이터 수집: yfinance로 실제 주식 데이터
- 특징 생성: 이동평균, 변동성, 변화율
- 모델 학습: 선형 회귀, 랜덤 포레스트 등
- 평가: MAE, RMSE, R²
- 시각화: 실제 vs 예측 그래프
🧪 도전 과제
1. 다른 종목 예측해보기
NAVER, 카카오 등 다른 종목으로 시도해보세요.
2. 더 좋은 모델 찾기
XGBoost, LightGBM 등 고급 모델을 시도해보세요.
3. 앱으로 만들기
Flask로 웹 앱을 만들어 실시간 예측을 해보세요.
💡 초보자를 위한 실전 팁
Tip 1: 에러가 나면 당황하지 마세요!
흔한 에러와 해결법:
1
2
3
4
5
6
7
8
9
10
11
# 에러 1: "No data found, symbol may be delisted"
→ 티커 심볼 확인!
'삼성전자' ❌ → '005930.KS' ✅
# 에러 2: "KeyError: 'Close'"
→ 데이터가 비어있음
period="1y" 대신 period="6mo" 시도
# 에러 3: "ValueError: Input contains NaN"
→ 결측치 처리 필요
df = df.dropna() # 또는 fillna()
Tip 2: 작은 것부터 시작하세요!
단계별 접근:
1
2
3
4
5
6
7
8
# 1단계: 간단한 특징만 (처음)
features = ['Close', 'Volume']
# 2단계: 이동평균 추가 (익숙해지면)
features = ['Close', 'Volume', 'MA_5', 'MA_20']
# 3단계: 고급 특징 (자신감 있을 때)
features = ['Close', 'Volume', 'MA_5', 'MA_20', 'Volatility', 'RSI']
처음엔 작게, 잘 되면 확장!
Tip 3: 결과를 너무 믿지 마세요!
1
2
3
4
5
# 훈련 R² = 0.95 # 와! 대박?
# 테스트 R² = 0.30 # 어...
→ 정상입니다! 주가는 예측하기 매우 어려워요.
→ 이 프로젝트는 **머신러닝 실습**이지 **투자 도구**가 아닙니다!
현실적인 기대치:
- R² > 0.5: “오! 생각보다 괜찮네?” 😊
- R² > 0.7: “정말 잘 했어요!” 🎉
- R² > 0.9: “의심해보세요” 🤔 (과적합 가능성)
Tip 4: 데이터 기간을 조정해보세요!
1
2
3
4
5
6
7
8
# 너무 짧으면 (< 3개월)
→ 데이터 부족, 학습 어려움
# 적당히 (6개월 ~ 1년)
→ 연습하기 좋음! ✅
# 너무 길면 (> 5년)
→ 시간 오래 걸림, 시장 상황 변화
Tip 5: 시각화를 적극 활용하세요!
1
2
3
4
5
6
7
8
9
# 숫자만 보면 이해 어려움
print(f"MAE: {mae:.2f}") # 2534.67 → 뭔 뜻?
# 그래프로 보면 명확!
plt.plot(y_test, label='실제')
plt.plot(y_pred, label='예측')
plt.legend()
plt.show()
# → "아, 전반적인 추세는 맞추는구나!"
🤔 자주 묻는 질문 (FAQ)
Q1: yfinance 설치가 안 돼요!
A: 여러 해결 방법이 있습니다!
1
2
3
4
5
6
7
8
9
10
11
12
13
# 방법 1: pip 업그레이드
python -m pip install --upgrade pip
pip install yfinance
# 방법 2: 관리자 권한 (Windows)
pip install yfinance --user
# 방법 3: 캐시 삭제
pip cache purge
pip install yfinance
# 방법 4: 특정 버전 설치
pip install yfinance==0.2.28
Q2: 한국 주식 티커를 어떻게 찾나요?
A: 종목 코드 + “.KS” (코스피) 또는 “.KQ” (코스닥)! 📈
자주 사용되는 티커:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 코스피 (KOSPI)
'005930.KS' # 삼성전자
'000660.KS' # SK하이닉스
'005380.KS' # 현대차
'035420.KS' # NAVER
'051910.KS' # LG화학
# 코스닥 (KOSDAQ)
'035720.KQ' # 카카오
'263750.KQ' # 펄어비스
# 미국 주식 (심볼만)
'AAPL' # 애플
'MSFT' # 마이크로소프트
'GOOGL' # 구글
'TSLA' # 테슬라
찾는 방법:
- 네이버 증권에서 종목 코드 확인
- 코스피 →
.KS추가 - 코스닥 →
.KQ추가
Q3: 예측 정확도가 너무 낮아요. 정상인가요?
A: 완전히 정상입니다! 주가 예측은 세계에서 가장 어려운 문제 중 하나예요! 📉
현실적인 수준:
1
2
3
4
5
6
7
8
9
10
11
12
# 초보자 프로젝트
R² = 0.3 ~ 0.5 # "잘 하고 있어요!" ✅
MAE = 2000 ~ 5000원 (삼성전자 기준)
# 고급 프로젝트 (LSTM 등)
R² = 0.5 ~ 0.7 # "매우 훌륭해요!" 🎉
# 전문가 수준
R² = 0.7 ~ 0.8 # "논문 쓸 수 있어요!" 📝
# 의심스러운 수준
R² > 0.95 # "과적합 아닐까?" 🤔
주가 예측이 어려운 이유:
- 랜덤 워크 이론: 주가는 예측 불가능 (노벨상 이론)
- 외부 요인: 뉴스, 정책, 세계 경제 (데이터에 없음)
- 비선형성: 복잡한 패턴
- 시장 효율성: 모두가 아는 패턴은 이미 가격에 반영
이 프로젝트의 목표:
- ❌ 돈 벌기
- ✅ 머신러닝 파이프라인 실습
- ✅ 데이터 전처리 연습
- ✅ 모델 평가 경험
Q4: shuffle=False는 왜 필요한가요?
A: 시계열 데이터는 시간 순서가 중요하기 때문! ⏰
문제 상황:
1
2
3
4
5
6
# shuffle=True (잘못된 방법)
# 훈련: 2024-06-01, 2024-08-15, 2024-04-20, ...
# 테스트: 2024-07-10, 2024-05-03, ...
# → 미래 데이터로 과거를 예측! (치팅)
# → 테스트에 "미래" 정보 노출
올바른 방법:
1
2
3
4
5
# shuffle=False
# 훈련: 2024-01-01 ~ 2024-08-31 (과거)
# 테스트: 2024-09-01 ~ 2024-12-31 (미래)
# → 과거로 미래 예측 (현실적)
시각적 비유:
1
2
3
4
5
타임머신 X:
[과거 데이터] → 학습 → [미래 예측] ✅
타임머신 O:
[미래 데이터도 포함] → 학습 → [예측] ❌
코드 예시:
1
2
3
4
5
6
7
8
9
# ✅ 올바른 방법
train_size = int(len(df) * 0.8)
train_data = df[:train_size] # 앞 80%
test_data = df[train_size:] # 뒤 20%
# 또는
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, shuffle=False
)
Q5: Target을 shift(-1)로 만드는 이유가 뭔가요?
A: “오늘 데이터로 내일 가격 예측”하기 위해서! 🔮
개념 이해:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 원래 데이터
Date Close
0 2024-01-01 70000
1 2024-01-02 71000
2 2024-01-03 72000
# shift(-1) 적용
df['Target'] = df['Close'].shift(-1)
# 결과
Date Close Target
0 2024-01-01 70000 71000 ← 1월1일 데이터로 1월2일 예측
1 2024-01-02 71000 72000 ← 1월2일 데이터로 1월3일 예측
2 2024-01-03 72000 NaN ← 미래가 없음
학습 과정:
1
2
3
4
5
# 모델이 배우는 것
X: 1월1일의 가격, 거래량, 이동평균 등
y: 1월2일의 가격 (Target)
→ "아, 이런 패턴이면 다음 날 이렇게 되는구나!"
주의사항:
1
2
3
# 마지막 행은 NaN이 됨
df['Target'] = df['Close'].shift(-1)
df = df.dropna() # NaN 제거 필수!
Q6: 실제 투자에 사용해도 되나요?
A: 절대 NO! 이것은 학습용 프로젝트입니다! 🚫💰
이 프로젝트의 한계:
- 너무 단순한 특징
1 2
# 우리: Close, MA_5, Volume # 전문가: 수백 개의 기술 지표, 뉴스 감성 분석, 거시경제 지표 등 - 최신 데이터 미반영
1 2
# 모델: "과거 패턴을 배움" # 현실: "코로나, 전쟁, 정책 변화 등" (패턴 깨짐) - 거래 비용 미고려
1 2
# 우리: 수수료, 세금 무시 # 현실: 매매마다 0.5% 수수료 + 세금 - 심리적 요인
1 2
# 모델: "냉정한 예측" # 사람: "공포와 탐욕" (예측대로 못 함)
대신 이렇게 활용하세요!:
✅ 학습 목적:
- 머신러닝 파이프라인 이해
- 데이터 전처리 연습
- 모델 평가 실습
✅ 포트폴리오:
- GitHub에 업로드
- 이력서에 프로젝트로 추가
- 면접에서 설명
✅ 추가 공부:
- 금융 공학 (Quantitative Finance)
- 알고리즘 트레이딩 (Algorithmic Trading)
- 딥러닝 (LSTM, Transformer)
실제 투자를 하려면:
- 전문 교육 (CFA, FRM 등)
- 실전 경험 (모의 투자)
- 리스크 관리
- 법률/규제 이해
📚 다음 학습
Day 99: 종합 프로젝트 2 - AI 챗봇 만들기 ⭐⭐⭐⭐
내일은 ChatGPT API를 활용한 AI 챗봇 프로젝트입니다!
“주식 예측은 어렵지만, 머신러닝 학습엔 완벽한 주제입니다!” 🚀
Day 98/100 Phase 10: AI/ML 입문 #100DaysOfPython
