본문 바로가기

TIL 통합

03/05 TIL 실전프로젝트ing

오늘 한 일

LSTM, 아리마 모델 알아보기

Prophet 성능 테스트 코드 짜기


Python

<Prophet을 활용한 주가 예측 및 예상 수익률 알고리즘 구축하기>


*주말 작업

import yfinance as yf
import pandas as pd
import numpy as np
from prophet import Prophet
import matplotlib.pyplot as plt
from datetime import datetime
plt.style.use('fivethirtyeight')


#주가추세 및 향후 1년 시계열 예측 보여주는 함수

def showmetheprophet(corp_name, 원화투자금):
  current_datetime = datetime.now().strftime("%Y-%m-%d")
  corp = yf.download(corp_name, start='2014-03-01', end=current_datetime) #입력 날짜 기준으로 주식 데이터 다운로드
  corp['ds'] = pd.to_datetime(corp.index, format = '%Y-%m-%d')
  corp['y'] = corp['Adj Close']                           #조정 마감가를 y에 할당
  corp = corp[['ds', 'y']]
  #모델 적합
  model_prophet = Prophet(changepoint_prior_scale = 0.15, daily_seasonality = True)
  model_prophet.fit(corp)

  #향후 1년간의 time stamp 생성

  fcast_time = 365 #365일 예측
  corp_forecast = model_prophet.make_future_dataframe(periods = fcast_time, freq = 'D')
  corp_forecast = model_prophet.predict(corp_forecast)
  model_prophet.plot(corp_forecast, xlabel = 'Date', ylabel= 'adj price($)')

  #투자금에 따른 주식 구매량 및 수익률
  #1)입력한 현재 날짜 기준 금액 구하기
  current_price = corp_forecast.query('ds == @current_datetime').iloc[0]['yhat']
  dollor = 원화투자금/1360
  #주문가능 수량 및 잔여금
  amount = dollor//current_price
  purchasable_price = round(amount*current_price, 2)
  residue = round(dollor%current_price,2)
  print("현재 날짜 기준 주문 가능 수량은 약", amount, "개이며, 총", purchasable_price, "달러입니다. 잔여금은", residue, "달러입니다")


  #예상 수익(평균) 및 수익률(평균)
  avg_f_price = corp_forecast.iloc[-1]['yhat']    #yhat칼럼의 맨 마지막 행 값 가져오기
  expected_avg_total_price = round(amount*avg_f_price,2)
  expected_avg_profit = round(expected_avg_total_price-purchasable_price, 2)
  avg_profit_percentage = round((avg_f_price-current_price)/current_price*100, 2)
  print("1년 후 총금액의 예상 평균치는", expected_avg_total_price, '달러이며, 그에 따른 수익은', expected_avg_profit, '입니다. 예상 수익률은', avg_profit_percentage, '% 입니다.' )

  #예상 수익(최대) 및 수익률(최대)
  upper_f_price = corp_forecast.iloc[-1]['yhat_upper']    #yhat칼럼의 맨 마지막 행 값 가져오기
  expected_upper_total_price = round(amount*upper_f_price,2)
  expected_upper_profit = round(expected_upper_total_price-purchasable_price, 2)
  upper_profit_percentage = round((upper_f_price-current_price)/current_price*100, 2)
  print("1년 후 총금액의 예상 최대치는", expected_upper_total_price, '달러이며, 그에 따른 수익은', expected_upper_profit, '입니다. 예상 수익률은', upper_profit_percentage, '% 입니다.' )

  #예상 수익(최저) 및 수익률(최저)
  lower_f_price = corp_forecast.iloc[-1]['yhat_lower']    #yhat칼럼의 맨 마지막 행 값 가져오기
  expected_lower_total_price = round(amount*lower_f_price,2)
  expected_lower_profit = round(expected_lower_total_price-purchasable_price, 2)
  lower_profit_percentage = round((lower_f_price-current_price)/current_price*100, 2)
  print("1년 후 총금액의 예상 최저치는", expected_lower_total_price, '달러이며, 그에 따른 수익은', expected_lower_profit, '입니다. 예상 수익률은', lower_profit_percentage, '% 입니다.' )

 

이렇게 입력하면

showmetheprophet('TYO', 10000000)

이렇게 나온다!

 

 

일단 주말동안 위처럼 작업을 했고, 오늘은 이어서 예측력 측정 코드를 작성해봤다.

 

 

<Prophet으로 예측력 측정하기 - MAE값 및 그래프 출력>

1. 과정

tyo_train = toyota[:-251]
tyo_test = toyota[-251:]

model = Prophet()
model.fit(tyo_train)

# 예측
future = tyo_test[['ds']]  # 테스트 데이터의 날짜 칼럼을 그대로 사용하여 future 데이터프레임 생성
forecast = model.predict(future)

# 평가
# 여기서는 간단하게 MAE(Mean Absolute Error)를 계산하여 평가합니다.
# 실제로는 주식 예측의 경우 더 다양한 평가 지표를 사용할 수 있습니다.
y_pred = forecast['yhat'].values
y_true = tyo_test['y'].values
from sklearn.metrics import mean_absolute_error
mae = mean_absolute_error(y_true, y_pred)
print('MAE: %.3f' % mae)


#출력값 - MAE: 1.117

MAE 값이 1.117이면 꽤나 괜찮은 값인가 싶다가도 애초에 10년 동안의 range가 7.5~22.5 안팎이라 뛰어나다고는 말하기 애매하지 않나 싶었다. 그래서 그래프를 이어서 출력해봤는데

 

import matplotlib.pyplot as plt

plt.plot(y_true, label='Actual')
plt.plot(y_pred, label='Predicted')
plt.legend()
plt.show()

이런 그래프가 나왔다. 전체적인 추세를 러프하게 맞추긴 했지만, 제대로 예측했다고는 보기 힘든 결과인 듯 하다. 그래서 회의 때 내일 오전까지 파라미터를 이용해서 예측력을 높일 수 있는지 한 번 알아보고 작업해 보기로 함. 그렇게 해서 더 좋은 결과가 나오든 말든 기존의, 시계열 예측과 예상 수익을 출력해주는 알고리즘에 예측력까지 출력해주는 기능을 추가해야겠다.

 

 

2. 어려웠던 부분

1)train데이터와 test데이터 나누기

tyo_train = toyota[:-251]
tyo_test = toyota[-251:]

처음 봤던 블로그 글에서는 train용과 test용을 나누는 거를 train 기준 drop함수를 사용해서 다음처럼 표현했어서 조금 헷갈렸었다. 틀린 것은 아니지만, 다음 둘이 같음을 늘 숙지할 것!

tyo_train = toyota.drop(toyota.index[-251:]) #뒤에서부터 251번째까지를 제외

tyo_train = toyota[:-251] #첫 행부터, 뒤에서부터 251행 직전까지를 포함

 

 

2)train데이터의 예측 날짜 생성

# 예측
future = tyo_test[['ds']]  # 테스트 데이터의 날짜 칼럼을 그대로 사용하여 future 데이터프레임 생성
forecast = model.predict(future)

오늘 한시간 넘게 애먹었던 부분.

 

처음에 참고한 블로그 글의 train데이터와 test데이터는 자동차 판매량을 예측하는 것으로, 단위가 일이 아닌 달이어서 적당히 숫자만 입력해주면 됐는데, 주식 데이터의 경우 주말을 제외한 일수만 카운트해서 칼럼을 만들어야하기 때문에 다소 애를 먹었다.

처음에는

future = model.make_future_dataframe(periods=len(tyo_test))

이렇게 입력했는데 결과에서 날짜 매칭이 계속 안 된다고 뜨길래 그대로 막힐뻔 했고,

 

그러다가 진짜로 날짜 매칭이 안 된다는 것을 깨닫고는 매칭을 해줘야겠다 싶어서 다음처럼 입력했는데도 이상하게 계속 결과값이 다르게 나와서 또 날짜가 매칭이 안 된다는 에러 메시지만 계속 나왔다.

# freq='B'를 사용하여 평일만 생성
future = model.make_future_dataframe(periods=len(tyo_test), freq='B')


# freq='B'를 사용하여 평일만 생성
future = pd.DataFrame({'ds': pd.date_range(start=tyo_test['ds'].iloc[0], end=tyo_test['ds'].iloc[-1], freq='B')})

그렇게 한시간을 넘게 헤매다가 챗 지피티한테 그냥 'tyo_test'의 날짜 데이터를 그대로 칼럼으로 쓸 수 없어?라고 하니 다음처럼 알려줬다.

 

생각해보면 간단한 내용인데.....나 뭐가 문제였던 걸까..?

# 예측
future = tyo_test[['ds']]  # 테스트 데이터의 날짜 칼럼을 그대로 사용하여 future 데이터프레임 생성
forecast = model.predict(future)

 

'TIL 통합' 카테고리의 다른 글

03/07 TIL 실전 프로젝트 ing  (1) 2024.03.07
03/06 TIL 실전프로젝트 ing  (2) 2024.03.06
02/29 TIL 실전프로젝트 1일차  (0) 2024.02.29
2/28 수 TIL  (0) 2024.02.28
02/27 화 TIL  (0) 2024.02.27