본문 바로가기
실전 투자 기법

실전 투자 전략 (21) - 머신러닝 알고리즘(kNN, SVM, Decision tree)을 이용한 절대수익 전략(2)

by systrader79 2016. 11. 2.
반응형

 지난 포스팅에서는 머신 러닝의 개념에 대해서 대략적으로 살펴봤는데, 이번 포스팅에서는 지도 학습 머신 러닝 알고리즘 중 가장 대표적이면서도 널리 이용되는 분류 알고리즘인 kNN 알고리즘과 SVM (support vector machine) 알고리즘을 이용한 절대 수익 투자 기법을 만들어 보겠습니다. 

 

1. 분류 알고리즘이란?

 분류 알고리즘이란, 구분되지 않은 전체 데이터를, 학습과정을 통해 비슷한 속성을 지니고 있는 데이터들로 나누는 방법을 익힌 후, 새로 받아들이는 데이터를 학습한 기준에 따라 분류하는 것을 의미합니다. 

 분류 알고리즘 중 가장 대표적인 것은 kNN(k-nearest neighbor) 알고리즘과 SVM(support vector machine)이 있는데, 각각을 살펴보겠습니다. 


2. kNN (k-nearest neighbor) 알고리즘

 kNN 알고리즘은 새로 받아들인 데이터를 판단해야 할 경우, 기존에 학습한 데이터와 비교하여 가장 가까운 거리의 데이터의 분류값으로 받아들이는 알고리즘입니다.  

 말이 어렵죠? 다음의 예를 살펴보면 대번에 이해가 가실 겁니다. 


 여러분이 어떤 영화를 멜로 영화와 액션 영화로 구분하고 싶다고 가정해보겠습니다. 

 어떤 기준으로 구분할 수 있을까요? 다양한 기준이 있겠지만, 아주 단순무식하게 영화 전체에서 확인되는 키스신의 개수와 발차기의 개수로 구분을 해보겠습니다. 키스신이 많으면 멜로물일 것이고, 발차기 횟수가 많다면 액션 영화일 가능성이 높겠죠? 키스신도 많은데 발차기 회수까지 많다면 아마 막장드라마겠죠..


  <장서희의 포효.jpg>

 (물론 이 기준이 완벽한 것은 아니기에, 이 알고리즘에 완벽을 기대해서는 안됩니다.)  

 그래서, 컴퓨터에게 학습을 시키기 위해 우리가 이미 잘 알고 있는 멜로물과 액션물의 데이터를 주고 학습을 시켰습니다. 학습시킨 자료는 다음과 같습니다. 



  이를 좌표 평면상에 표시하면 다음과 같습니다.

  x 축은 발차기 횟수, y축은 키스 횟수입니다. 보시는 바와 같이 발차기 횟수와 키스신의 상대적인 차이에 따라 우하측에는 액션영화, 좌상측에는 멜로물로 구분되는 것을 볼 수 있습니다. 



 그렇다면, 여기서 물음표로 표시된 데이터(발차기 18회,키스 90회)에 해당하는 영화는 무엇으로 구분해야 할까요? 대략 눈대중으로 보면 액션물보다는 멜로물 쪽에 치우쳐 있으니, 멜로물일 가능성이 높다고 어림짐작할 수 있습니다. 

 하지만, 이를 좀 더 객관적이고 정량적으로 판단하는 기준은 무엇이 될까요? 

 여기서 이용되는 알고리즘이 바로 kNN 알고리즘입니다. 단어의 뜻이 의미하는 바와 같이 kNN 알고리즘에서는 판단해야 할 데이터를 받아들이면, 이 데이터를 학습 데이터가 표시된 공간에서 거리상 가장 가까운 k 개의 데이터를 찾아낸 후, 다수결 투표를 한 뒤 어디에 속할지 결정을 합니다. 여기서 k 값은 연구자가 임의로 선정을 합니다. k 값이 너무 낮으면 민감하지만, 노이즈에 걸리기 쉽고, k가 너무 크면 반대로 노이즈에는 강하지만, 민감도가 떨어진다는 단점이 있어 적절한 수준의 k 값을 이용하는 것이 중요합니다. 

 위의 예에서 한 번 확인해 볼까요? 우리가 분류하고자 하는 미지의 영화에 나온 발차기 횟수는 18회, 키스 횟수는 90회였군요. 그러면 이 점을 위 좌표 평면에 표시한 뒤, 이 점과 기존에 학습 데이터에 이용된 모든 점과의 개별적인 거리를 각각 측정합니다. 점과 점 사이의 거리는 중학교 3학년 수학에서 배운 피타고라스의 공식(좌표 평면에서 두 점사이의 거리)을 이용해서(Euclidian distance) 구할 수 있습니다. 이렇게 미지의 영화와 모든 영화와의 거리를 잰 뒤 가장 가까운 순으로 정렬하면 다음과 같습니다. 

 


 여러분이 k 값을 3으로 정했다면 거리가 가장 가까운 3개의 영화를 대상으로 다수결 투표를 한다고 보시면 됩니다. 3개 영화 모두 멜로물이므로, 우리가 알고자 하는 영화도 멜로물이라고 추정할 수 있습니다. 

 3개중 2개는 멜로물이고, 1개는 액션물이었다면? 다수결의 원칙에 따라 멜로물로 분류가 되는 것이지요. 


 

 kNN 알고리즘은 얼핏 보면 허접해보여도 단순한 만큼 상당히 탄탄한 성능을 보여주는 알고리즘입니다. 


3. SVM(support vector machine)

 Support vector machine은 이질적인 서로 다른 집단간의 상대적인 거리를 최대화 할 수 있는 기준면을 통해 분류하는 방식입니다. 

 


 위 그림에서 세모군과 동그라미군을 정확하게 구분하기 위해서는 세모와 동그라미의 가장 바깥쪽에 위치하고 있는 경계점(이를 support vector라고 합니다)을 중심으로 이들 간의 거리를 최대화 할 수 있는 경계선을 그어야 하겠지요? 

그림상에서 직관적으로도 확인할 수 있듯이 A 점선보다는 B 실선이 두 집단을 구분하는데 훨씬 더 이상적이라 생각할 수 있겠습니다. 왜냐면, A로 구분할 경우 새로 판단해야 할 데이터(실제로는 동그라미)가 A 경계에 살짝 세모쪽으로 벗어나면 세모로 잘못 분류될 가능성이 높기 때문입니다. 그래서, 서포트벡터 간의 수직 거리를 최대화하는 평면을 구하여 두 그룹으로 구분하는 방법이 support vector machine 알고리즘입니다. 

 SVM 알고리즘은 명백한 이론적 근거에 기반을 두므로, 결과 해석이 용이하고, 적은 학습 데이터만으로도 효과가 있으며, 인공 신경망 수준의 퍼모먼스를 보인다는 강력한 장점이 있어 대단히 광범위하게 이용되는 알고리즘 중의 하나입니다. 


4. 이제 본론으로! kNN과 SVM 알고리즘을 이용한 절대 수익 전략 

 이제 본격적으로 kNN과 SVM 알고리즘을 이용한 절대 수익 전략을 만들어보겠습니다. 

 두 가지 알고리즘 모두 지도 학습 기반의 분류 알고리즘이지요? 

 지도학습이라함은, 일단 교육 과정에서는 이것이 똥인지 된장인지 컴퓨터에게 알려줘야 한다는 것인데요, 그러기 위해서는 두가지 조건이 필요합니다.  

 첫째, 똥과 된장을 구분하는 기준을 알려줘야한다.  

 둘째, 적어도 학습 기간 동안에는 똥인지 된장인지 답을 줘야 한다. 


 여기서 살펴볼 알고리즘은 코스피와 S&P 500 지수의 월간 데이터를 기준으로 한 전략입니다. 

 최근 12개월 간 데이터가 학습 과정에 이용되고, 이를 토대로 다음달 수익이 플러스일지, 마이너스를 분류합니다. 

 즉, 다음달 수익이 플러스일지 마이너스일지를 구분하는 작업이 똥과 된장을 구분하는 작업이라고 한다면, 과연 똥과 된장을 구분하는 기준은 무엇이 되어야 할까요? 바로 이것은 여러분이 어떤 기준으로 정하느냐에 달린 문제로 정답이 없습니다. 그렇기 때문에, 머신 러닝 알고리즘도 근본적으로는 수익률에 미치는 변수를 어떤 것으로 정하느냐에 따라 무수히 많은 전략이 가능합니다. 

 여기서는 최근 12개월 간의 프랙탈 모멘텀을 평균 낸 값(횡보를 보정한 상태의 상승 추세 강도를 반영)과 최근 12개월 변동성을 학습 기준(feature)로 정하고, 다음달의 수익률을 predicted value로 정한 뒤 매달 최근 12개월 간의 데이터로 학습을 시켰습니다. 만일  다음 달 수익이 플러스일 것으로 분류되면 주식 보유, 마이너스일 것으로 생각되면 현금 보유(연 3% 수익 기준) 전략을 취했으며, 매월 이렇게 나온 수익 곡선과 현금을 1:1로 리밸런싱하는 전략을 짜 보았습니다. 

 

결과입니다. 

* kNN - S&P500

 * kNN - KOSPI

* SVM - S&P 500

* SVM - KOSPI


 대략 보았을 때 이 데이터상에서는 SVM 보다는 더 단순한 kNN 알고리즘의 성과가 우수한 것으로 확인됩니다. (물론 이것이 절대적으로 우월하다는 의미는 아닙니다.) 

 전반적으로 분류 정확도는 60~70% 정도로 계산됩니다. 생각보다 수익률이 높지 않다고 생각할 수 있겠지만, 100% 주식에 투자한 것이 아니고 현금이 최소 50% 이상 투자된 구조를 고려하면 무난한 수준이라고 볼 수 있습니다.  

 기본적으로 추세와 횡보를 반영하는 지표와 변동성을 반영하는 지표로 만든 아주 단순한 전략인데요, 기본적으로 모멘텀을 반영하는 지표를 feature로 선정하였기 때문에, 수익 곡선도 추세 추종 전략의 수익 곡선과 매우 유사한 형태를 보여주는 것을 확인할 수 있습니다. 

 

 이번 포스팅에서 살펴본 전략은 실제 투자에 적용할 정도로 영양가 있는 고급 전략이라기보다는 머신 러닝의 개념을 어떤 식으로 실제 투자에 접목할 수 있는지 정도를 확인할 수 있는 간단한 예시 정도로 보시면 됩니다. 

 좀 더 발전적인 전략을 짜기 위해서는 feature (독립변수)를 어떻게 선정하고, 어떤 predicted feature (종속 변수) 를 이용하고, 알고리즘의 세부적인 파라미터를 어떻게 세팅하느냐에 따라 무궁무진한 변형이 가능하고, 본 블로그에서 소개한 기본적인 자산 배분 전략과도 결합시키면 다양한 응용이 가능하리라 생각합니다.


다음은 파이썬 코드입니다. 


import pandas as pd

import matplotlib.pyplot as plt

import numpy as np

import pandas_datareader.data as web

from sklearn import neighbors, svm

from sklearn.ensemble import RandomForestClassifier


def price(stock, start):

    price = web.DataReader(name=stock, data_source='yahoo', start=start)['Adj Close']

    return price.div(price.iat[0]).resample('M').last().to_frame('price')

    

def fractal(a, p):

    df = pd.DataFrame()

    for count in range(1,p+1):

        a['direction'] = np.where(a['price'].diff(count)>0,1,0)

        a['abs'] = a['price'].diff(count).abs()

        a['volatility'] = a.price.diff().abs().rolling(count).sum()

        a['fractal'] = a['abs']/a['volatility']*a['direction']

        df = pd.concat([df, a['fractal']], axis=1)

    return df


def meanfractal(a, l=12):

    a['meanfractal']= pd.DataFrame(fractal(a, l)).sum(1,skipna=False)/l


a = price('^K11','2000-01-01')

a['cash'] = [(1.03**(1/12))**x for x in range(len(a.index))]

a['meanfractal']= pd.DataFrame(fractal(a, 12)).sum(1,skipna=False)/12   

a['rollingstd'] = a.price.pct_change().shift(1).rolling(12).std()

a['result'] = np.where(a.price > a.price.shift(1), 1,0)     

a = a.dropna()


print(a)


clf = neighbors.KNeighborsClassifier(n_neighbors=3)

clf1 = svm.SVC()

clf3 = RandomForestClassifier(n_estimators=5)


a['predicted']= pd.Series()

for i in range(12,len(a.index)):

    x  =  a.iloc[i-12:i,6:8]    

    y  =  a['result'][i-12:i] 

    clf.fit(x, y)

    a['predicted'][i]= clf.predict(x)[-1] 


a = a.dropna()

a.price = a.price.div(a.price.ix[0])

print(a)

accuracy=clf.score(a.iloc[:,6:8],a['result'])


a['결과'] = np.where(a.predicted.shift(1)==1,a.price/a.price.shift(1),1).cumprod()

a['result'] = np.where(a.predicted.shift(1)==1,(a.price/a.price.shift(1)+1.0026)/2,1.0026).cumprod()

a['동일비중'] = ((a.price/a.price.shift(1)+1.0026)/2).cumprod()

a[['result','price']].plot()

plt.show()

print ("Predicted model accuracy: "+ str(accuracy))


나가시기 전에 블로그 글 맨 하단의 배너 광고를 한 번만 클릭해주시면 더 좋은 콘텐츠를 만드는데 큰 도움이 됩니다~ 감사합니다! >


1. 네이버 카페 '실전주식투자 연구소' (클릭) 으로 오시면, 본 블로그의 모든 내용을 순서대로 확인하실 수 있고, 다양한 실전 투자 정보도 얻을 수 있습니다~


2. 'systrader79의 주식 단기 매매 전략 온라인 강좌'가 뉴지스탁에서 진행 중입니다!

   개별 주식을 이용한 단기 매매 기법, 뉴지스탁을 통한 완전 자동 투자 매매 구현에 관한 폭넓         은 노하우를 다루고 있으니, 많은 성원 부탁드립니다~

   첫 번째 강의는 수강 신청없이 무료로 시청 가능합니다 (아래 링크 클릭 --> 1번 방송 클릭)

     강의 소개 (클릭)

     * 강의 바로가기 (클릭)



3. 여러분의 인생이 걸린 너무나도 중요한 소식! --- > 여기를 클릭하세요!


반응형

댓글6

  • jin 2016.12.31 16:57

    a=price('^KS11','2000-01-01')
    (KS11 이 맞지 않나요?)

    그리고 아래와 같은 오류가 나는데....
    뭔지 모르겠어요.

    27 a['meanfractal']=pd.DataFrame(fractal(a,12)).sum(1, skipna=False)/12
    18 a['fractal']=a['abs']/a['volatility']*a['direction']


    raceback (most recent call last):
    File "/Users/lineplus/PycharmProjects/pytube/stock79_predict.py", line 27, in <module>
    a['meanfractal']=pd.DataFrame(fractal(a,12)).sum(1, skipna=False)/12
    File "/Users/lineplus/PycharmProjects/pytube/stock79_predict.py", line 18, in fractal
    a['fractal']=a['abs']/a['volatility']*a['direction']
    답글

  • Favicon of https://stock79.tistory.com BlogIcon systrader79 2016.12.31 18:00 신고

    ^KS11가 맞습니다~ 야후 티커가 원래 그렇더라구요~
    답글

  • 박정수 2017.05.09 06:08

    아래와 같이 입력을 했는데...

    import pandas as pd
    import matplotlib.pyplot as plt
    import numpy as np
    import pandas_datareader.data as web

    다음과 같은 에러가 나왔어요....

    ImportError Traceback (most recent call last)
    <ipython-input-2-1f9ac6f42cb7> in <module>()
    2 import matplotlib.pyplot as plt
    3 import numpy as np
    ----> 4 import pandas_datareader.data as web

    ImportError: No module named 'pandas_datareader'

    제가 Python에 초보이고, Windows버전의 Anaconda를 사용하고 있는데 Linux의 경우에 해당하는 pip을 이용한 설명되고 있어 진도가 나가질 않네요...


    답글

  • 오혜성 2019.04.29 13:55

    감사합니다

    답글

  • ㅇㅇ 2019.07.10 18:24

    코드를 쓰시기 전에 미리 한번 실행하고 나서 올려주세요. 다른 사람 헷갈리게 하지 말고.
    답글

  • 롱쑛 2019.09.15 16:59

    질문드릴게 있는데, 위의 코드와 같이 한다면 일종의 치팅이 아닌가요? 학습용 데이터와 백테스트용 데이터가 같은 것 같아서요.
    답글