사실 주가는 우리같은 개인투자가 모르는 호재, 악재가 있고,
꼭 호재, 악재가 있다고해서 주가가 상승하거나 하락하지도 않고,
이런것들이 주가에 선반영 되기도 하고,
따라서 나는 딥러닝, 머신러닝으로 주가를 예측한다는건 어렵다고 생각하지만 그래도 궁금하니 그냥 재미삼아 해봤다.
라이브러리
미국주식 일단위 주가를 받아오기 위해 yahoo finance api를 설치한다.
pip install yahoo-finance
정규화, 학습/검증/평가 데이터 분할, 평가 등을 위해 scikit-learn을 설치한다.
pip install scikit-learn
모델구현을 위해 tensorflow를 설치한다(tensorflow.keras를 사용한다).
pip install tensorflow
차트를 그리기 위해 matplotlib, seaborn을 설치한다.
pip install matplotlib
pip install seaborn
코드
참고로 일 단위 예측이다
추후 일주일, 한달 단위 예측을 하기위해 PRED_DAY에 예측 일 수를 입력하도록 작성해놓긴했다.
모델도 PRED_DAY에 원하는 예측 일 수를 설정하면 학습과 예측은 정상적으로 실행되나,
차트를 그리는 부분은 일 단위 예측 기준이다.
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
import yfinance as yf
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, mean_absolute_error
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
TICKER = 'AAPL' # 티커명
WINDOW_SIZE = 60 # 60일 단위 그룹
PRED_DAY = 1 # 예측 일 수 (60일 전 피쳐를 입력받아 다음날 주가 예측)
TRAIN_RATE = 0.90 # 학습 비율
SAVE_WEIGHT = f'./weights/{TICKER}_model.h5' # 가중치 저장 경로
SAVE_CHART = f'./images/{TICKER}.png' # 정답-예측 차트 저장 경로
EPOCH = 50 # 학습 횟수
EARLYSTOP_PATIENCE = 10 # 10회 동안 모델이 개선되지 않으면 학습 그만
FEATURE = ['Open', 'High', 'Low', 'Close', 'Volume'] # 학습에 사용할 피쳐 컬럼들
TARGET = ['Close'] # 예측할 컬럼
# window size 만큼 일 단위 피쳐, 타겟을 그룹
def make_dataset(data, label, window_size=30, pred_day=1):
feature_list, label_list = [], []
for i in range(len(data) - window_size - pred_day):
feature_list.append(np.array(data.iloc[i:i+window_size]))
label_list.append(np.array(label.iloc[i+window_size:i+window_size+pred_day]))
return np.array(feature_list), np.array(label_list)
df = yf.Ticker(TICKER).history(period="max").reset_index() # ticker의 일 단위 정보를 가져온다(dataframe형태)
print('Get YF data: ', df.shape)
values = df['Close'].values # 나중에 역변환을 위해 전체 종가를 저장해둔다
# Minmax 정규화 해준다
scaler = MinMaxScaler()
data_df = pd.DataFrame(columns=FEATURE, data=scaler.fit_transform(df[FEATURE]))
# 학습, 평가셋을 나눈다
train_num = int(len(df) * TRAIN_RATE)
train, test = data_df.iloc[:train_num], data_df.iloc[train_num:]
print(f'\ntrain test split - train: {train.shape}, test: {test.shape}')
# window size 만큼 일 단위 피쳐, 타겟을 그룹핑해준다
train_feature, train_label = make_dataset(train[FEATURE], train[TARGET], WINDOW_SIZE, PRED_DAY)
test_feature, test_label = make_dataset(test[FEATURE], test[TARGET], WINDOW_SIZE, PRED_DAY)
print('\nGroub by window size')
print(f'train_feature: {train_feature.shape}, train_label: {train_label.shape}')
print(f'test_feature: {test_feature.shape}, test_label: {test_label.shape}')
# 나중에 예측 date를 표시하기위해 저장해둔다.
test_date = list(df['Date'])[-test_label.shape[0]:]
# 학습셋에서 다시 검증셋 비율을 설정한다.
x_train, x_valid, y_train, y_valid = train_test_split(
train_feature, train_label, test_size=0.2
)
print('\nTrain test split')
print(f'x_train: {x_train.shape}, y_train: {y_train.shape}')
print(f'x_valid: {x_valid.shape}, y_valid: {y_valid.shape}')
# 간단한 모델을 만든다.
model = Sequential()
model.add(LSTM(WINDOW_SIZE, return_sequences=True, input_shape=(train_feature.shape[1:])))
# model.add(Dropout(0.2))
model.add(LSTM(64, return_sequences=False))
# model.add(Dropout(0.2))
model.add(Dense(PRED_DAY, activation='linear'))
model.compile(loss='mse', optimizer='rmsprop')
# model.summary()
early_stop = EarlyStopping(
monitor='val_loss', # val_loss를 모니터링 해서
patience=EARLYSTOP_PATIENCE # 10회 개선되지 않으면 그만
)
checkpoint = ModelCheckpoint(
SAVE_WEIGHT, # 가중치 저장 경로
monitor='val_loss', # val_loss 모니터링
verbose=1, # 학습 진행상황 표출
save_best_only=True, # 베트스 가중치만 저장
mode='auto'
)
# 학습 시작
model.fit(
x_train,
y_train,
batch_size=16,
epochs=EPOCH,
validation_data=(x_valid, y_valid),
callbacks=[early_stop, checkpoint]
)
print('\nfinished training...')
###########################################
# Load weights
model.load_weights(SAVE_WEIGHT) # 가중치를 불러온다.
# 정답 shape을 예측값과 같게
pred = model.predict(test_feature)
test_label = test_label.reshape(pred.shape)
# 평가 rmse, mae
rmse = round(mean_squared_error(test_label, pred, squared=False), 4)
mae = round(mean_absolute_error(test_label, pred), 4)
print(f'RMSE: {rmse}\nMAE: {mae}')
# 펼치기
test_label, pred = test_label.flatten(), pred.flatten()
# 실제 값(주가)으로 역변환
test_label = test_label * (values.max() - values.min()) + values.min()
pred = pred * (values.max() - values.min()) + values.min()
result_df = pd.DataFrame(
data={
'Date': test_date,
'Close': test_label,
'Pred': pred
}
)
tmp = list(result_df['Date'])
ts_start, ts_end = str(tmp[0]).split(' ')[0], str(tmp[-1]).split(' ')[0] # 테스트셋 시작 종료 date
# 그리기
plt.figure()
plt.title(f'{TICKER}: {ts_start}~{ts_end}')
sns.lineplot(data=result_df, x='Date', y='Close', label='Answer') # 실제 주가
sns.lineplot(data=result_df, x='Date', y='Pred', label='Predict') # 예측 주가
plt.xticks(rotation=45)
plt.legend()
plt.savefig(SAVE_CHART)
plt.show()
결과
차트에 표시된 일자는 학습에 사용하지 않은 데이터들이다.
값들을 자세히 보면 그다지 정확하진 않지만, 전반적인 흐름을 쫓아가는게 신기하긴했다.
아 참고로 오늘(한국시간 기준 2021년 5월 4일) 테슬라 예측 주가는 555달러다... ㅋㅋ
'개발' 카테고리의 다른 글
맥(Mac)에서 간단하게 SSH 서버 접속하기 (0) | 2021.05.10 |
---|