본문 바로가기
Project/Predict Stock price Project

3. 주식 데이터 보조지표 추가하기 - TA패키지 (주가 예측 프로젝트)

by inhovation97 2022. 3. 11.

학부 연구생으로서 진행한 주가 예측 프로그램의 일련의 과정을 차례로 포스팅합니다. 

위 그림처럼 어떤 종목이든 10일간의 데이터로

다음날 종가 상승 여부를 예측하는 머신러닝 프로젝트입니다.

 

주식 데이터는 여기저기 예제들도 굉장히 많이 있고,

비교적 얻기 쉬운 빅데이터이기 때문에 여러가지 프로젝트를 하시는 분들은 굉장히 많을 거라고 생각합니다. 

 

하지만 어려운 도메인인만큼 유의미한 모델링을 한 사례는 굉장히 적습니다. 

특히 주식을 잘 모르시는 분들에게는 정말 어려운 데이터죠...

제가 얻은 인사이트를 기록합니다.

데이터 수집 - EDA - 전처리 - 모델링 - 성능 개선 

이번 포스팅은 캔들에 대한 정보 외에 다른 보조 지표를 추가하는 방법에 대해 포스팅합니다.

 

1. 보조 지표를 추가해야 하는 이유
2. ta 라이브러리 이용하여 보조지표 추가하기

 

1. 보조 지표를 추가해야 하는 이유

 

보조 지표를 추가하는 이유는 시가 / 고가 / 저가 / 종가 / 거래량 변수 5개로는 데이터의 정보가 너무 부족합니다.

심지어 저는 5개 변수 외에  5일 / 20일 / 60일 / 120일 이동 평균 값 총 9개의 변수로 모델링을 해봤는데, ( 스케일링 진행함 ) 기존 머신러닝 모델 ( svm, 로지스틱, RF, 여러 gradient boosting 모델 등등...)은 제대로 예측하지 못했으며 시계열 데이터로 변환하여 LSTM을 진행했는데, 위 이미지처럼 아예 수렴하지 않는 결과가 나왔습니다. 

epoch가 얼마 가지 않아 loss가 줄지 않더라구요...

 

물론 하이파 파라미터들을 제대로 설정해주지는 않았지만, LSTM은 나름 신경 써봤는데도 garbage in garbage out 이라는 말이 떠오르더라구요...

( LSTM 모델링 코드입니다. )

어떤 식으로 데이터 shape을 만들어주었는지는 다음 포스팅에서 하고, 이번 포스팅은 보조 지표를 추가하는 내용을 다룰겁니다.

 

삼성 전자의 캔들 차트입니다. 

5일선은 해당 종목의 종가에 대한 5일의 평균선을 의미합니다.

불린저 밴드는 이미지에 크게 보이는 노란색 바운더리인데, n일의 종가 평균 * 2std로 알고 있습니다.

아무튼 주식 데이터는 캔들에 대한 정보 말고도 5일선/20일선/60일선/120일선 / 불린저 밴드 등등 여러가지 지표가 수십가지가 있습니다. 

현재 모델이 학습할 수 없을 정도로 데이터가 쓰레기이므로, TA라는 라이브러리를 통해 여러가지 지표를 추가해봅시다.

 

2. ta 라이브러리 이용하여 보조지표 추가하기

 

ta라이브러리 공식 홈페이지만 봐도 보조지표가 엄청나게 많습니다.

 

<이동 평균선 만들기>

import FinanceDataReader as fdr
stock_df = fdr.DataReader('000210', '2017').reset_index()

ma = [5,20,60,120]
for days in ma:
    stock_df['ma_'+str(days)] = stock_df['Close'].rolling(window = days).mean()

stock_df

결과

먼저 이동평균선부터 만들어줍시다. 

( finance data reader에 대한 포스팅은 여기 가시면 됩니다. )

 

이동 평균선은 TA라이브러리를 쓰지 않고, Rolling 함수를 이용하여 간단히 생성해주었습니다.

이동 평균선은 n일 동안의 종가 평균 값이기 때문에 앞에 n-1개의 값이 NaN값으로 들어갑니다. 따라서 ma 선을 이용하려면 제가 포스팅했던 내용대로 원하는 train셋보다 start 시점을 조금 많이 땡겨오면 됩니다.

 

<여러 보조지표 추가하기>

import ta
import FinanceDataReader as fdr
stock_df = fdr.DataReader('000210', '2017').reset_index()

H, L, C, V = stock_df['High'], stock_df['Low'], stock_df['Close'], stock_df['Volume']

# stock_df['bol_high'] = ta.volatility.bollinger_hband(C)
# stock_df['bol_low']  = ta.volatility.bollinger_lband(C)
stock_df['MFI'] = ta.volume.money_flow_index(
    high=H, low=L, close=C, volume=V, fillna=True)

stock_df['ADI'] = ta.volume.acc_dist_index(
    high=H, low=L, close=C, volume=V, fillna=True)

stock_df['OBV'] = ta.volume.on_balance_volume(close=C, volume=V, fillna=True)
stock_df['CMF'] = ta.volume.chaikin_money_flow(
    high=H, low=L, close=C, volume=V, fillna=True)

stock_df['FI'] = ta.volume.force_index(close=C, volume=V, fillna=True)
stock_df['EOM, EMV'] = ta.volume.ease_of_movement(
    high=H, low=L, volume=V, fillna=True)

stock_df['VPT'] = ta.volume.volume_price_trend(close=C, volume=V, fillna=True)
stock_df['NVI'] = ta.volume.negative_volume_index(close=C, volume=V, fillna=True)
stock_df['VMAP'] = ta.volume.volume_weighted_average_price(
    high=H, low=L, close=C, volume=V, fillna=True)

# Volatility
stock_df['ATR'] = ta.volatility.average_true_range(
    high=H, low=L, close=C, fillna=True)
stock_df['BHB'] = ta.volatility.bollinger_hband(close=C, fillna=True)
stock_df['BLB'] = ta.volatility.bollinger_lband(close=C, fillna=True)
stock_df['KCH'] = ta.volatility.keltner_channel_hband(
    high=H, low=L, close=C, fillna=True)
stock_df['KCL'] = ta.volatility.keltner_channel_lband(
    high=H, low=L, close=C, fillna=True)
stock_df['KCM'] = ta.volatility.keltner_channel_mband(
    high=H, low=L, close=C, fillna=True)
stock_df['DCH'] = ta.volatility.donchian_channel_hband(
    high=H, low=L, close=C, fillna=True)
stock_df['DCL'] = ta.volatility.donchian_channel_lband(
    high=H, low=L, close=C, fillna=True)
stock_df['DCM'] = ta.volatility.donchian_channel_mband(
    high=H, low=L, close=C, fillna=True)
stock_df['UI'] = ta.volatility.ulcer_index(close=C, fillna=True)
# Trend
stock_df['SMA'] = ta.trend.sma_indicator(close=C, fillna=True)
stock_df['EMA'] = ta.trend.ema_indicator(close=C, fillna=True)
stock_df['WMA'] = ta.trend.wma_indicator(close=C, fillna=True)
stock_df['MACD'] = ta.trend.macd(close=C, fillna=True)
stock_df['ADX'] = ta.trend.adx(high=H, low=L, close=C, fillna=True)
stock_df['-VI'] = ta.trend.vortex_indicator_neg(
    high=H, low=L, close=C, fillna=True)
stock_df['+VI'] = ta.trend.vortex_indicator_pos(
    high=H, low=L, close=C, fillna=True)
stock_df['TRIX'] = ta.trend.trix(close=C, fillna=True)
stock_df['MI'] = ta.trend.mass_index(high=H, low=L, fillna=True)
stock_df['CCI'] = ta.trend.cci(high=H, low=L, close=C, fillna=True)
stock_df['DPO'] = ta.trend.dpo(close=C, fillna=True)
stock_df['KST'] = ta.trend.kst(close=C, fillna=True)
stock_df['Ichimoku'] = ta.trend.ichimoku_a(high=H, low=L, fillna=True)
stock_df['Parabolic SAR'] = ta.trend.psar_down(
    high=H, low=L, close=C, fillna=True)
stock_df['STC'] = ta.trend.stc(close=C, fillna=True)
# Momentum
stock_df['RSI'] = ta.momentum.rsi(close=C, fillna=True)
stock_df['SRSI'] = ta.momentum.stochrsi(close=C, fillna=True)
stock_df['TSI'] = ta.momentum.tsi(close=C, fillna=True)
stock_df['UO'] = ta.momentum.ultimate_oscillator(
    high=H, low=L, close=C, fillna=True)
stock_df['SR'] = ta.momentum.stoch(close=C, high=H, low=L, fillna=True)
stock_df['WR'] = ta.momentum.williams_r(high=H, low=L, close=C, fillna=True)
stock_df['AO'] = ta.momentum.awesome_oscillator(high=H, low=L, fillna=True)
stock_df['KAMA'] = ta.momentum.kama(close=C, fillna=True)
stock_df['ROC'] = ta.momentum.roc(close=C, fillna=True)
stock_df['PPO'] = ta.momentum.ppo(close=C, fillna=True)
stock_df['PVO'] = ta.momentum.pvo(volume=V, fillna=True)

stock_df

이동 평균선 4개 까지 추가한 데이터

일일이 공홈가서 변수 지정하여 추가해줍니다...

생각해야할 점은 이 지표들은 fdr로 1기업을 불러오면 그 기업마다 컬럼을 추가해줘야합니다. 

수 천개 기업들이 전부 stack돼 있는 데이터프레임에서 추가해주면 안됩니다. 기업마다 액면가가 다르기 때문에 꼭 fdr로 한 기업을 불러오면 해당 변수를 추가하여 데이터들을 stack해주어야합니다. 

 

이 지표들이 전부 무슨 뜻인지는 알 필요없습니다! 다만 계산 상으로 모든 기업이 똑같은 동일한 스케일의 값을 갖는 보조지표도 있는 반면, 기업의 액면가에 따라 달라지는 보조 지표들은 꼭 체크를 해두셔야합니다. ( 가령, 이동 평균선은 기업의 액면가에 따라 스케일이 달라짐) 

그런 보조 지표들은 나중에 스케일링할 때 같이 진행해야 하기 때문입니다. (매우 중요)

스케일링 관련 포스팅

 

이제 4~50개의 추가 정보를 얻게 되었네요.

 

댓글