반응형
##########################################주가 예측 ##################################################

# 예제의 목적 : "상관 자산" + "과거 데이터" => "MS 주가 예측"

""" ############################################################################################ 1번 문제 정의 ############################################################################################"""
# MS 과거데이터 + 독립변수로 사용되는 상관 자산 : (주식 + 환율 + 인덱스)

""" ############################################################################################2번 문제 데이터 불러오기############################################################################################"""
import numpy as np
import pandas as pd
from sklearn.metrics import mean_squared_error
import matplotlib.pyplot as plt

#################################################
###############데이터 불러오기####################
'''
stk_tickers = ['MSFT','IBM','GOOGL']  # 주식 데이터 (MSFT,IBM,GOOGL)
ccy_tickers = ['DEXJPUS','DEXUSUK'] # 환율 데이터 (DEXJP US, DEXUS UK)
idx_tickers = ['SP500','DJIA','VIXCLS'] # 인덱스 지수 데이터 (SP500,DJIA,VIXCLS 받아옴.)
stk_data = web.DataReader(stk_tickers,'yahoo')
ccy_data = web.DataReader(ccy_tickers, 'fred')
idx_data = web.DataReader(idx_tickers,'fred')
'''
#################################################
############CSV파일에서 불러오기##################
stk_data = pd.read_csv("./data_collection/datasets/orelly_MSFT IBM GOOGL.csv",index_col=[0],header=[0,1]) # index_col -> default (None) // 0열을 인덱스로 불러온다. // header -> default (첫번째 행) // 0~1행을 열이름으로 지정해서 불러온다.
ccy_data = pd.read_csv("./data_collection/datasets/orelly_DEXJPUS DEXUSUK.csv",index_col=[0]) # index_col -> default (None) // 0열을 인덱스로 불러온다. 
idx_data = pd.read_csv("./data_collection/datasets/orelly_SP500 dJIA VIXCLS.csv",index_col=[0]) # index_col -> default (None) // 0열을 인덱스로 불러온다. 

##################################################
##################################################
"""
##################stk_data###########

               GOOGL       IBM   DEXJPUS   DEXUSUK     SP500      dJIA    VIXCLS   MSFT_DT  MSFT_3DT  MSFT_6DT  MSFT_12DT
2017-04-11 -0.014996 -0.022835 -0.009071  0.003772 -0.002707 -0.001835  0.245454 -0.003810  0.019586  0.023174   0.049428
2017-04-19  0.019607 -0.053524 -0.004392  0.022857 -0.002887 -0.009141 -0.054737 -0.006743 -0.003836  0.009889   0.038547
...              ...       ...       ...       ...       ...       ...       ...       ...       ...       ...        ...
"""

"""

"""
return_period = 5 # 5일 지연 변수를 사용하기 위해 
Y = np.log(stk_data.loc[:,('Adj Close','MSFT')]).diff(return_period).shift(-return_period) 
"""
Adj Close 멀티 인덱스에서 MSFT 주가 column만을 긁어 온다. (loc[:,('Adj Close','MSFT)])
diff(return_period)이므로 5일기준으로 이산(차이)을 구한다.
shift(-return_period)이므로 Date 인덱스기준으로2009-12-31 위의 튜플로 주가 값을 올린다.
Date
2009-12-31    0.005888
2010-01-04   -0.022216
2010-01-05   -0.029168
2010-01-06   -0.013744
2010-01-07    0.016610
                ...
2019-12-24         NaN
2019-12-26         NaN
2019-12-27         NaN
2019-12-30         NaN
2019-12-31         NaN
"""
Y.name = Y.name[-1] + '_pred' ### Y.name = MSFT

# Y와 동일하게 진행
X1 = np.log(stk_data.loc[:, ('Adj Close', ('GOOGL','IBM'))]).diff(return_period)
"""
Adj close 멀티 인덱스에서 GOOGL,IBM 주가 column만을 긁어온다.
diff(return_period)이므로 5일 기준으로 이산(차이)를 구한다.

Attributes Adj Close
Symbols        GOOGL       IBM
Date
2009-12-31       NaN       NaN
2010-01-04       NaN       NaN
2010-01-05       NaN       NaN
2010-01-06       NaN       NaN
2010-01-07       NaN       NaN
...              ...       ...
2019-12-24 -0.007750  0.005646
2019-12-26  0.007781  0.003713
2019-12-27 -0.001328  0.005263
2019-12-30 -0.008555 -0.020716
2019-12-31 -0.008357 -0.011202
"""
X1.columns = X1.columns.droplevel() # 코드상에서는 Attributes Adj Close 삭제됨 // droplevel이란 특정 레벨의 attribute를 삭제. 

X2 = np.log(ccy_data).diff(return_period)

"""
##################X2 결과###########################
DEXJPUS   DEXUSUK
DATE
2017-04-04       NaN       NaN
2017-04-05       NaN       NaN
2017-04-06       NaN       NaN
2017-04-07       NaN       NaN
2017-04-10       NaN       NaN
...              ...       ...
2022-03-21  0.009699  0.011964
2022-03-22  0.020683  0.014664
2022-03-23  0.021212  0.008819
2022-03-24  0.030186  0.001822
2022-03-25  0.023306  0.001821
"""
X3 = np.log(idx_data).diff(return_period)

"""
##################X3 결과###########################
SP500      dJIA    VIXCLS
DATE
2017-04-04       NaN       NaN       NaN
2017-04-05       NaN       NaN       NaN
2017-04-06       NaN       NaN       NaN
2017-04-07       NaN       NaN       NaN
2017-04-10       NaN       NaN       NaN
...              ...       ...       ...
2022-03-28  0.025307  0.011593 -0.181217
2022-03-29  0.026248  0.013887 -0.193720
2022-03-30  0.032283  0.025015 -0.198316
2022-03-31  0.002265 -0.000853 -0.052581
2022-04-01  0.000616 -0.001233       NaN
"""
### concat 함수는 사용법에 따라 튜플을 추가하거나, attribute를 추가하는 형식으로 사용이 가능하다.
### 아래 X4의 경우, MSFR_DT 기준으로, 5일 지연 변수, 30일, 60일 기준 행렬 생성 ###
X4 = pd.concat([np.log(stk_data.loc[:,('Adj Close','MSFT')]).diff(i)\
    for i in [return_period, return_period*3,\
        return_period*6,return_period*12]],axis=1).dropna()
X4.columns = ['MSFT_DT','MSFT_3DT','MSFT_6DT','MSFT_12DT'] # multi column -> single column(['MSFT_DT','MSFT_3DT','MSFT_6DT','MSFT_12DT']) 됨 (X기준으로 설명한 내용)
"""
##################X4 결과###########################
MSFT_DT  MSFT_3DT  MSFT_6DT  MSFT_12DT
Date
2010-03-30 -0.003688  0.033126  0.048874  -0.018904
2010-03-31 -0.012216  0.010986  0.024190  -0.050461
2010-04-01 -0.028733 -0.000685  0.006537  -0.055233
2010-04-05 -0.013236  0.000000  0.017230  -0.045312
2010-04-06 -0.009166  0.001024  0.020328  -0.033151
...              ...       ...       ...        ...
2019-12-24  0.017240  0.052639  0.077701   0.127371
2019-12-26  0.027475  0.057192  0.079315   0.149732
2019-12-27  0.020657  0.058484  0.079510   0.169371
2019-12-30  0.001143  0.037762  0.065776   0.148682
2019-12-31  0.001841  0.041033  0.053656   0.135968
"""
X = pd.concat([X1,X2,X3,X4],axis=1) # axis=1인 경우 오른쪽에 추가 한다고 생각하면 된다.

dataset = pd.concat([Y,X],axis=1).dropna().iloc[::return_period,:] # dropna란 NA(NULL 정해지지 않은 값)을 삭제 // iloc[::return_period,:]????????????????????


Y=dataset.loc[:,Y.name] ### Y의 shape (129,)
X=dataset.loc[:,X.columns] ### X의 shape (129,11)



""" ############################################################################################ 3.2 탐색적 데이터 분석 (데이터 시각화) ############################################################################################"""



""" ############################################################################################ 5. 모델 평가 ##########################################################################################"""
from statsmodels.tsa.arima.model import ARIMA
validation_size = 0.2 ###### 80%를 (train_set)/// 20%를 (train_set)
train_size = int(len(X) * (1-validation_size)) ## len(X)는 129 // train_size 103

X_train, X_test = X[0:train_size],X[train_size:len(X)]
Y_train, Y_test = Y[0:train_size],Y[train_size:len(Y)]
"""
########## X ###########

               GOOGL       IBM   DEXJPUS   DEXUSUK     SP500      dJIA    VIXCLS   MSFT_DT  MSFT_3DT  MSFT_6DT  MSFT_12DT
2017-04-11 -0.014996 -0.022835 -0.009071  0.003772 -0.002707 -0.001835  0.245454 -0.003810  0.019586  0.023174   0.049428
2017-04-19  0.019607 -0.053524 -0.004392  0.022857 -0.002887 -0.009141 -0.054737 -0.006743 -0.003836  0.009889   0.038547
...              ...       ...       ...       ...       ...       ...       ...       ...       ...       ...        ...
[129 rows x 11 columns] 예시

########## Y ###########

2017-04-19    0.042002
2017-04-27    0.007879
                ...
Name: MSFT_pred, Length: 129, dtype: float64                

"""

### 테스트 옵션 및 평가 메트릭.(4장 참고)
num_folds = 10 #K-fold(10겹) N 겹 진행 초기 변수 설정
scoring = 'neg_mean_squared_error' # sklearn은 MSE에 음수를 취해서 오차를 점수로 바꾼다. 즉, MSE와 같은 개념 점수가 클수록 좋은 결과값.

X_train_ARIMA = X_train.loc[:,['GOOGL','IBM','DEXJPUS','SP500','dJIA','VIXCLS']] ###교재는 DJIA -> dJIA
X_test_ARIMA = X_test.loc[:,['GOOGL','IBM','DEXJPUS','SP500','dJIA','VIXCLS']] ### 교재는 DJIA -> dJIA

#print("길이: {}".format(len(X_test_ARIMA))) # (103,6)

tr_len= len(X_train_ARIMA) # 103
te_len = len(X_test_ARIMA) # 26
to_len = len(X) # 129 (103+26)

####### ARIMA(Autoregressive integrated moving average) #########
modelARIMA=ARIMA(endog=Y_train,exog=X_train_ARIMA,order=[1,0,0]) # endog = 내생 변수 / order : 사용할 AR 매겨변수 ( p,d,q) / exog 외생 변수
## p는 자기 회귀의 차수, d는 첫 디퍼런싱의 정도, q는 이동평균 부분의 차수이다.

model_fit = modelARIMA.fit() # 해당 학습 데이터로 학습

error_Training_ARIMA = mean_squared_error(Y_train,model_fit.fittedvalues) # MSE 도출  => sigma(Y_train-model_fit.fittedvalues)^2 값 // fittedvalues : ## 추정값 (skit-learn 라이브러리에서만 가능한것으로 보임)
predicted = model_fit.predict(start = tr_len - 1, end = to_len -1,exog= X_test_ARIMA)[1:] # ## 예측값 도출 (자세한 사용법 확인 필요.)
error_Test_ARIMA = mean_squared_error(Y_test,predicted) # MSE 도출
print(error_Test_ARIMA) ## 0.00030985051584437283

seq_len = 2 # LSTM에 대한 시퀸스 길이 (자세한 역할 확인 필요!!!!!!!!!!!!!!!!!)
Y_train_LSTM, Y_test_LSTM = np.array(Y_train)[seq_len-1:], np.array(Y_test) #Y_train의 [swq_len-1:] 인덱스만 Y_train_LSTM에 포함 , 나머지는 test_LSTM으로 만듬.
X_train_LSTM = np.zeros((X_train.shape[0]+1-seq_len,seq_len, X_train.shape[1])) # np.zeros로 0으로만 이루어진 행렬 생성
X_test_LSTM = np.zeros((X_test.shape[0],seq_len,X.shape[1])) # 동일
#print(X_train.shape) (103,11)

for i in range(seq_len): # iter문을 사용해서 2차원 -> 3차원으로 변경하는 작업
    X_train_LSTM[:,i,:] = np.array(X_train)[i:X_train.shape[0]+i+1-seq_len,:]
    X_test_LSTM[:,i,:] = np.array(X)[X_train.shape[0]+i-1:X.shape[0]+i+1-seq_len,:]
#(X_test_LSTM.shape) (102,2,11)
#(X_train_LSTM.shape) (26, 2, 11)



#LSTM 망
from keras.models import Sequential
from keras.layers import Dense
from keras.optimizer_v1 import SGD ### instead of optimizers import SGD
from keras.layers import LSTM
names = []
test_results = []
train_results = []

##### model -> model.add() -> ... -> model.compile -> 
#### 큰틀의 학습 방범 model 구축 -> fit 함수로 모델 학습-> predict로 학습된 모델에 예측값을 넣어 예측값 반환
def create_LSTMmodel(learn_rate = 0.1,momentum=0):
    #모델 생성
    model = Sequential()
    model.add(LSTM(50,input_shape=(X_train_LSTM.shape[1],X_train_LSTM.shape[2])))
    #필요시 더 많은 셀 추가
    model.add(Dense(1)) # 출력 뉴런(노드)의 수  결정(마지막 레이어의 노드의 갯수는 출력 데이터 y의 갯수와 동일하게 1개로 지정.) // (additional argument는 ) ### https://ebbnflow.tistory.com/120
    optimizer = SGD(lr=learn_rate,momentum=momentum) #많이 쓰는 otpimizer 함수 : SGD(스토캐스틱 graident 함수) // 파아미터는 : lr(학습률), momentum : 모멘텀   /// 교재에서는 써놓고 사용을 안하는 변수
    model.compile(loss='mse',optimizer='adam') #옵티마이저는 adam 사용 // 손실함수는 mse
    return model  # model 값을 리턴

LSTMModel = create_LSTMmodel(learn_rate=0.01,momentum=0) # 함수 실행
LSTMModel_fit = LSTMModel.fit(X_train_LSTM,Y_train_LSTM,validation_data=(X_test_LSTM,Y_test_LSTM),epochs=330,batch_size=72,verbose=0,shuffle=False) # fit함수로 학습데이터 학습


plt.plot(LSTMModel_fit.history['loss'],label='train',) # 오차율 기록
plt.plot(LSTMModel_fit.history['val_loss'],'--',label='test',) # val_loss // test함수에서 정확도 측정
plt.legend() #범례설정(위에서 선언한 label들 보이게 호출)
plt.show()



error_Training_LSTM = mean_squared_error(Y_train_LSTM,LSTMModel.predict(X_train_LSTM)) # predict함수로 train에서의 MSE의 예측값을 error_training_LSTm 변수에 저장.
predicted = LSTMModel.predict(X_test_LSTM) # 주가 예측 데이터에서 예측값 변수에 저장.
error_Test_LSTM = mean_squared_error(Y_test,predicted) # 실제 주가와 predicted과의 괴리율을 저장??

test_results.append(error_Test_ARIMA)
test_results.append(error_Test_LSTM)
train_results.append(error_Training_ARIMA)
train_results.append(error_Training_LSTM)
names.append("ARIMA")
names.append("LSTM")


def evaluate_arima_model(arima_order):
    #predicted = list
    modelARIMA = ARIMA(endog=Y_train,exog=X_train_ARIMA,order=arima_order)
    model_fit = modelARIMA.fit()
    error = mean_squared_error(Y_train, model_fit.fittedvalues) # fittedvalues : ## 추정값
    return error

# ARIMA 모델에 대한 p,d,q 값 조합 평가
## 아래 evaluate 모델은 약식 함수임(세개의 변수만으로는 평가가 완벽하지 않음.)
def evaluate_models(p_values,d_values,q_values):
    best_score,best_cfg = float("inf"), None
    for p in p_values:
        for d in d_values:
            for q in q_values:
                order = (p,d,q)
                try:
                    mse = evaluate_arima_model(order)
                    if mse < best_score: # best_score보다 MSE(오차)가 낮을 경우 더 좋은 값이므로 
                        best_score, best_cfg = mse, order # 변수를 업데이트한다.
                    print('ARIMA%s MSE=%.7f' % (order,mse))
                except:
                    continue
    print('Best ARIMA%s MSE=%.7f' % (best_cfg, best_score))

#매개변수 평가
import warnings
p_values = [0,1,2]
d_values = range(0,2)
q_values = range(0,2)
warnings.filterwarnings("ignore") #경고 메시지 숨기기
evaluate_models(p_values, d_values,q_values) # p,d,q 주어진 범위내에서 BEST값 추출.

##7.모델 확정
#모델 준비
modelARIMA_tuned = ARIMA(endog=Y_train,exog=X_train_ARIMA,order=[2,0,1]) # 가장 좋은 ARIMA 파라미터로 학습 진행.
model_fit_tuned = modelARIMA_tuned.fit()

#검증셋에 대한 정확도 추정
#estimate accuracy on validation set
predicted_tuned = model_fit.predict(start=tr_len-1,end=to_len-1,exog=X_test_ARIMA)[1:]
print(mean_squared_error(Y_test,predicted_tuned))


# 실제 데이터와 예측 데이터 그래프
predicted_tuned.index = Y_test.index # 실제 데이터와 예측 데이터 그래프의 index를 맞춘다.
plt.plot(np.exp(Y_test).cumprod(), 'r',label='actual',)  # exp(지수함수화.) # cumprod는 배열에서 주어진 축에 따라 누적되는 원소들의 누적 값을 계산하는 함수 ex) [1,2,3] => [1, 1*2, 1*2*3]


# t와 a 분리하여 그리기
plt.plot(np.exp(predicted_tuned).cumprod(),'b--',label='predicted')
plt.legend()
plt.rcParams["figure.figsize"] = (8,5) ## mlpt 전역 변수 
plt.show()
반응형

'IT 인터넷 > 머신러닝' 카테고리의 다른 글

파생 상품(옵션) - KFold  (0) 2022.05.17
주가예측 - KFOLD(교차검증-)  (0) 2022.05.16
아나콘다 dlib 설치 오류 해결방법  (0) 2022.01.15

+ Recent posts