강화학습 기반 주식 트레이딩 시스템

머신러닝 알고리즘 분류(유형)는 크게 목적(라벨) 변수 유무에 따라 지도학습과 비지도학습으로 분류한다. 또 하나  재미있는 분류로는 강화학습이 있다. 강화학습은 에이전트와 환경이 주어질 때, 에이전트가 행하는 행위에 대한 보상(reward)으로 학습한다. 데이터 1건1건마다 라벨링값이 주어지는 지도학습과 달리 강화학습에서는 행위에 대한 보상(지도학습에서의 라벨링 역할)이 매번 주어질 필요가 없다. 이를 지연 보상(delayed reward)이라고 한다. 따라서 매번 행위 때마다 reward를 할 필요없이 행위가 여러번 시행된 이후 결과만 보상해도 학습이 된다라는 사실이다. 강화학습을 사용한 바둑을 생각하면 이해가 될 것이다. 매수하다 보상이 아니라 승패 결과만 보상해도 된다.

트레이딩 관점에서의 강화학습

트레이딩 관점에서 강화학습을 생각하면 에이전트는 트레이더(trader)이고 에이전트의 행위는 사고 팔고 그리고 아무런 행위도 안하는 것이다. 사고 나서 아무런 행위를 안하면 산 종목을 보유하겠다는 것으로 해석 가능하다. 에이전트가 구별할 수 있는 환경(에이전트가 인식할 수 있는 상태값)은 주식에서 얻을 수 있는 데이터들이다. 가장 기본적으로 차트 데이터가 있고 기타로는 지표 (PER 등), 뉴스 등 다양한 것들이 있을 수 있다. 강화학습이 제대로 학습만 된다면 너무나도 좋다. 사고 팔고 할 때 매번 보상을 줄 필요 없이 일정 기간 동안의 수익률(바둑에서의 승패)만 가지고도 학습이 왠지 쉬워 보일 듯 하다. 지도학습처럼 살 때 팔 때 따로 따로 모델을 만들어서 학습할 필요없이 강화학습 에이전트의 행위 자체가 사고 파는 그리고 보유(무행위)하는 인공지능 모델이 한번에 만들어지니 얼마나 좋겠는가…

실전 경험

강화학습을 접한지는 오래되었지만, 주식에 접하면 어떨까 싶어 시도를 해본 경험은 몇년 전이다. 뉴스나 논문을 봐도 강화학습을 이용한 로봇 트레이딩 시스템이 종종 있다. 그러나 필자가 시도한 바로는 학습이 쉽지 않았다. 사고 팔고 보유하는 단일 모델로 트레이딩 시스템을 구현해보고 싶었으나 현재까지도 보류(지금은 강화학습보다는 지도학습으로 변경) 상태이다. 강화학습에 대한 이해가 부족해서인지 아니면 에이전트 입력값(환경 상태)을 잘 못 잡아서인지 어려웠다. 강화학습 구현에는 텐서플로우 기반 오픈 소스를 활용해 DQN 이라는 딥러닝 강화학습 알고리즘을 사용했다. 시중에 나와있는 책을 사서도 봤지만 학습이 되질 않았다. 왜 학습이 안될까? 여기서 학습이 안된다는 의미는, 에이전트가 보지 못한 테스트 데이터셋에 적용을 해서 수익률이 나지 않음을 의미한다.  내 나름 주식 환경에서 강화학습이 어려운 점을 정리해 본다.

  1. 상태 공간이 너무 커 DQN 망 학습이 어려움.
    결국 DQN의 입력값은 상태값이 되며, 출력값은 가치(value) 값인데 상태에 대한 가치 추정이 어려움
  2. 에이전트 독립적인 환경
    보통 강화학습에서의 에이전트와 환경의 관계에는 에이전트 행위에 따라 환경이 상태값을 리턴하며 최종 목적지에 도착할 경우 보상을 준다. 그리드(grid) 월드나 바둑을 생각하면 된다. 그러나 주식 환경에서는 에이전트 행위와 거의 상관없이 환경이 변한다. 수십만 수백만 에이전트가 환경에 물려 있어 실제 거래하고 있는 에이전트는 환경에 거의 영향을 줄 수 없다는 의미다. 다시 말해, 목적 지향적인 패스가 주어지고 최종 보상이 주어지는 환경이 아니라는 것이다. 랜덤워크로 변화하는 차트를 기반으로 에이전트 입장에서는 마구 잡이로 보상하는 환경에 대해서 에이전트가 과연 학습을 할 수 있겠는가?

필자의 실전 경험과 이해를 바탕으로 작성한 글이니 잘못 작성된 부분도 있을 것이다. 혹시 다른 의견을 가지고 계신 분이나 주식 환경에서의 강화학습이 가능하다라고 주장하시는 분, 기타 등등 관련 내용을 공유해 주시면 정말 고마울 것이다. ㅎ 정말로 궁금하다 과연 강화학습으로 수익이 날 수 있는지… 가끔 강화학습 기반 로보트레이더 기사를 보면 더욱 궁금해진다. 세계 최고 강화학습 기술을 보유하고 있는 그 유명한 딥마인드(DeepMind)는 왜 주식에 적용하지 않을까? ㅎㅎ

(강화학습에 관심있는 여러분의 의견 내지는 조언을 듣고 싶습니다.)

32/64비트 파이썬 프로세스 통신

필자는 대신증권 API(파이썬)으로 이용하고 있는 사용자 중 하나이다. 실제 파이썬 코드를 이용해 자동으로 매수/매도하는 프로그램을 운영하고 있다. 주식장이 열리는 날이면 매일 HTS 와 파이썬 모듈을 구동시키고 출근한다.

수익률이 크지는 않지만 (계속 보완/테스트 진행 중) 그래도 포기할만큼은 아니고 간간히 매수/매도로 수익이 나면 기분도 좋아지곤 한다.

이제는 2차 버전을 생각하고 있다. 수익률 때문은 꼭 아니지만 계속 새로운 것을 시도하면서 학습하고자 하는 목적도 있다. 바로 강화 학습 기반의 트레이딩 시스템이 다음 목표이다.

32/64비트 파이썬 통신이 필요한 이유

문제는 강화학습(DQN)을 하기 위해서는 텐서플로우(tensorflow)가 필요한데 대신증권 파이썬 모듈하고 돌리려니 문제가 발생했다. 텐서플로우는 64비트 환경에서 돌고 대신 증권은 32비트 환경에서만 구동해야 된다는 것이다. 텐서플로우를 32비트 환경에서 돌리는 것도 문제 해결은 될 수 있으나 32비트 환경에서 텐서플로우를 돌리기 위해서는 소스를 다운받아 컴파일해야 된다. 인터넷을 찾아보면 쉽지 않아 보인다. 한참을 좌절하고 있다가 우연히 IPC(Inter Process Communication)  관련 내용을 활용해보자라는 생각이 들었고 관련 자료를 찾아보기로 했다.

그러나 파이썬 기반 IPC 모듈이 딱히 마음에 드는 것이 없어 내 마음대로 필요한 내용을 구현해보고 사용해보고 있다. IPC도 아닌 fake IPC라고 스스로 명명해놓고 있다.

32/64비트 파이썬 기반 IPC 통신 방법

원리는 간단하다. 64비트 환경의 파이썬 모듈(예. 텐서플로우)에서 32비트 환경의 파이썬 모듈(대신증권 파이썬 모듈)을 호출한다. 그러면 대신증권의 파이썬 모듈에서 처리를 하고 나서 결과를 파일로 저장하고 이를 64비트 파이썬 모듈에서 읽어서 처리한다. 호출부 소스와 실행부 소스 일부는 아래와 같다.

호출부 (64비트 파이썬 환경)

# ...
from sytrap import fakeipc as fipc

sipc = fipc.SytrapFakeIpc()
p = sipc.call('get_item', itcode='A168330')  # get_item는 32비트 환경에서 구동
# ...

실행부 (32비트 파이썬 환경)

# ...
ret = syc.get_item(itcode)   
p.dump(ret, open(ippath, 'wb'))  # 처리 결과를 미리 약속된 경로에 저장
# ...

다만 호출할 때 64비트 환경에서 32비트 파이썬 실행기와 파이썬 모듈 경로를 정확히 제시해주어야 한다. 아래는 호출 방법의 예이다.

 <64비트 환경 python.exe> 64비트.py <32비트 환경 python.exe> 32비트.py

구현해야 할 양이 조금은 많아졌지만 (함수 옵션 처리, 파일 입출력 등) 드디어 64비트 텐서플로우를 이용해 32비트 대신증권 모듈을 호출할 수 있게 되어 마음껏 딥러닝 기반 트레이딩 시스템을 돌릴 수 있게 되었다. 수익률까지 좋아지길 기대하고 있다. ㅎ

저와 비슷한 고민을 하시는 분들에게 조금이나마 도움이 되길 바란다.

LDA 토픽 모델링

가끔 주식 관련 특징주 기사를 보면 주가에 영향을 줄만한 기사가 뜨는 경우가 있다. 물론 좋은 기사가 뜬다고 해서 주가가 항상 오르지는 않는다. 다만 테스트를 해보고 싶었다.

데이터 수집

먼저 기계 학습을 위해 관련 데이터 수집이 필요하다. 특정 사이트 URL를 선정해서 인터넷 기사와 주가와의 연관성 분석을 위해 5년간의 특징주 관련 기사를 수집했다. 그런 다음 기사가 뜬 시점의 주가와 2일 후 변동 주가를 기록하여 데이터를 수집하였다.

데이터 학습

데이터 학습은 LDA(Latent Dirichlet Allocation)라는 토픽 모델링 방법을 사용하였다. 신규 기사가 뜰 경우 LDA 토픽 벡터와 유사한 기사들을 검색하고 검색된 기사들의 주가 전후 변동값을 비교한 후, 신규 기사에 대한 주가 변동량으로 추정하는 방법을 이용하였다.

아래는 pseudo code


if new_article:
    tpv = lda_topic_vec(new_article)
    sims = find_similarity(tpv)
    ss = sort(sims, 10)  # get top 10 of similarity
    for article in ss:
        sumret += calc_change_rate(article)
    avgret = sumret/N
    if avgret > 0.02
        buy(ar)

LDA 란…

LDA는 잠재 디리클레 할당이라고 하는데, 문서에 잠재되어 있는 토픽(topic)을 이용해 분류(cluster)하는 방법론이다. 비지도 학습(unsupervised learning)의 일종이다. 나이브분류기(Naive Classifier)와 같은 예전 방법에는  문서를 포함하고 있는 단어들의 확률적인 분포로 문서를 분류했는데 LDA에서는 보이지 않은 토픽이라는 잠재(latent, hidden) 변수를 더 활용하는 점이 다르다. 단어는 직접적으로 확인 가능하지만 토픽은 추상적인 존재이기에 잠재라는 단어를 사용한다. 또 LDA는 디리클레라는 확률적인 분포를 가정하는 것이 특징이다. 반면 pLSA 와 같은 방법론은 확률적인 분포를 가정하지 않는다.

LDA 의 확률 분포는 다시 말해, 토픽 벡터의 요소가 양수이며 모든 요소를 더한 값이 1인 경우에 대해서 확률값이 정의되는 분포이다.

가령 토픽이 경제, 정치 2가지 뿐이라면 경제와 정치 2가지 토픽을 더해서 1이 되는 경우라 이해할 수 있다. 즉, 경제가 0.9, 정치가 0.1 정도의 토픽 비율의 문서는 가능한데, 경제 0.9, 정치 0.9 비율로 토픽이 구성된 문서는 사전 가정에서 배제하는 것이다.

위키에서 발췌한 아래 그림은 토픽 3개인 경우로 이해하면 되고 중앙이 큰 값을 가진다는 의미는 토픽이 어느정도 골고루 섞힌 문서가 많이 존재한다.(확률적으로 높은 값을 가짐)라는 의미로 해석할 수 있다.

(위키 그림 발췌)

 

LDA 토픽 모델링 결과, 주가에 긍적적인 기사 예

결과로만 보면, 신약 개발 관련 기업들의 기사 관련 내용들이 주가에 단기적으로 긍정적인 영향을 주는 것으로 파악되었다. 그래서 필자는 LDA 토픽 모델링 기법을 써서 특정 사이트를 모니터링 하다가 신약 개발 관련 기사들이 뜰 때는 자동으로 매수하도록 프로그램을 돌리기도 했다. 하지만 역시 소문에 팔고 기사에 팔라는 말이 맞는 듯…ㅎ

순환 신경망 (LSTM) 활용 주가 예측

시계열 데이터 예측에 좋은 성능을 낸다는 순환 신경망을 통해 제가 현재 보유하고 있는 내츄럴엔도텍 주식에 대한 예측을 해보고자 합니다. 내츄럴엔도텍은 한 때 백수오로 알려진 업체이며 최근 홈쇼핑 판매 개시로 많은 관심을 받고 있습니다.

 LSTM이란…

순환 신경망은 보통 RNN(Recurrent Neural Network)라고 하는데 그레디언트 소실(Gradient Vanishing Problem)을 해결하고자 메모리를 도입한 LSTM(Long-Short Term Memory)을 주로 이용합니다.

LSTM은 시계열 데이터 처리에 있어서는 ARIMA 등 기존 알고리즘보다 좋은 성능을 내는 것으로 알려져 있습니다. LSTM에 대한 자세한 내용은 여기를 참조하시기 바랍니다. 순환 신경망의 놀라운 효과 라는 안드레 카파시 블로그 글 (영문)도 참고하시기 바랍니다. 순환 신경망으로 세익스피어 글도 짓고 자바 프로그래밍도 합니다.

LSTM 소개에 대한 이미지 검색결과

기존 MLP 신경망은 은닉층의 노드값을 계산할 때 입력에 가중치를 곱해서 은닉층의 노드값(상태)값을 업데이트 합니다. 그러나 순환 신경망은 그와 달리 은닉층의 노드값을 계산할 때 이전 히든(hidden) 노드에 대한 상태값과 입력을 모두 활용합니다. (아래 수식 참조)

Alt text

 

 

이런 방법으로 은닉층 상태를 업데이트 하기에 시계열 데이터에서의 패턴 변화를 감지할 수 있게 됩니다. 또한 LSTM은 순환신경망에 메모리까지를 가지고 있어 과거 시계열 데이터의 패턴 변화를 더 잘 잡아내는 특징을 가지고 있습니다.

좀 더 자세히 설명을 드리면 LSTM은 메모리가 있으며 메모리 입출력 컨트롤을 위한 소자(논리적인 장치)가 있는데 게이트(gate)라고 합니다. 게이트에는 입력(input), 출력(output) 게이트 그리고 망각(forget) 게이트가 있습니다.

LSTM 게이트에 대한 이미지 검색결과

LSTM을 학습하는 방법은 조금 복잡합니다. 기본적으로는 BPTT (Backpropagation Through Time) 을 사용합니다. 알고리즘은 BP와 유사하지만 순환 신경망에서는 시계열 데이터를 다루다보니 그리고 은닉층이 이전 은닉층 상태값까지 고려하다보니 그로 인해 조금은 복잡합니다. 순환 신경망 그래프 안에 네트워크가 연결된 구조를 펼쳐(unfold) 오차에 준한만큼 역으로 따라가며 전파하여 가중치를 학습합니다. 자세한 내용은 밑바닥부터 시작하는 딥너링이라는 책을 한번 읽어보시면 좋을 듯 합니다. 또는 Graphically Determining Backpropagation Equations 블로그 자료를 참고하시길 바랍니다.

 학습 데이터

LSTM 학습에 사용한 데이터로는 아래와 같은 형태로 저장된 csv 포맷을 읽어서 처리했습니다(다운로드:  A168330) 미리 아래와 같은 포맷으로 데이터를 저장해두고 나서 학습한 것입니다. 그리고 간단히 종가(Close) 데이터만을 이용해서 학습했습니다.

Date Open High Low Close Volume
20170816 27250 32450 27100 31500 3341469
20170814 25700 26800 24400 26750 1384477
20170811 25300 25750 24050 24950 2019038

 케라스 활용 LSTM 구현

LSTM을 파이썬으로 돌리는 방법은 여러 가지가 있지만 많이 사용되는 케라스(Keras) 라이브러리를 이용했습니다. 케라스는 텐서플로우를 기반으로 쉽게 사용할 수 있도록 하기 위한 일종의 래핑(wrapping) 라이브러리 입니다.

import os
import pandas as pd
import numpy as np
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from sklearn.preprocessing import MinMaxScaler
import matplotlib.pyplot as plt
import math
from sklearn.metrics import mean_squared_error

look_back = 1
def create_dataset(dataset, look_back=1):
    dataX, dataY = [], []
    for i in range(len(dataset)-look_back-1):
        a = dataset[i:(i + look_back)]
        dataX.append(a)
        dataY.append(dataset[i + look_back])
    return np.array(dataX), np.array(dataY)

# file loader
sydtpath = "D:sydt"
naturalEndoTekCode = "A168330"
fullpath = sydtpath + os.path.sep + naturalEndoTekCode + '.csv'
pandf = pd.read_csv(fullpath, index_col="Date")

# convert nparray
nparr = pandf['Close'].values[::-1]
nparr.astype('float32')
print(nparr)

# normalization
scaler = MinMaxScaler(feature_range=(0, 1))
nptf = scaler.fit_transform(nparr)

# split train, test
train_size = int(len(nptf) * 0.9)
test_size = len(nptf) - train_size
train, test = nptf[0:train_size], nptf[train_size:len(nptf)]
print(len(train), len(test))

# create dataset for learning
trainX, trainY = create_dataset(train, look_back)
testX, testY = create_dataset(test, look_back)

# reshape input to be [samples, time steps, features]
trainX = np.reshape(trainX, (trainX.shape[0], 1, trainX.shape[1]))
testX = np.reshape(testX, (testX.shape[0], 1, testX.shape[1]))

# simple lstm network learning
model = Sequential()
model.add(LSTM(4, input_shape=(1, look_back)))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
model.fit(trainX, trainY, epochs=100, batch_size=1, verbose=2)

# make prediction
testPredict = model.predict(testX)
testPredict = scaler.inverse_transform(testPredict)
testY = scaler.inverse_transform(testY)
testScore = math.sqrt(mean_squared_error(testY, testPredict))
print('Train Score: %.2f RMSE' % testScore)

# predict last value (or tomorrow?)
lastX = nptf[-1]
lastX = np.reshape(lastX, (1, 1, 1))
lastY = model.predict(lastX)
lastY = scaler.inverse_transform(lastY)
print('Predict the Close value of final day: %d' % lastY)  # 데이터 입력 마지막 다음날 종가 예측

# plot
plt.plot(testPredict)
plt.plot(testY)
plt.show()

결과 화면

아래 차트 오른쪽 부분이 내츄럴엔도텍이 최근(2017년 8월) 홈쇼핑 판매 개시로 주가가 오르고 있는 부분입니다. 실제 주가(파란색)와 예측 주가(노란색)로 출력되어 있습니다. 예측은 현재일 (1일)을 이용해서 다음날 (1일)을 예측합니다. 마지막 날 예측이 어긋나는게 아쉽습니다

stocksLSTM2

위 소스에 대해 문의가 있거나 틀린 점, 보완할 점이 있으면 아래 댓글 남겨주시면 감사하겠습니다. 그리고 위 소스 내용은 아래 사이트(Machine Learning Mastery) 자료를 참고해서 개발한 것입니다.
Machine Learning Mastery

참고

가치 투자 및 관련 종목 검색 (대신증권 API 기반 파이썬 활용)

가치 투자라는 것은 성장 가능성만을 보고 투자하지는 않는다. 전망을 신뢰하기 보다 객관적인 자료로 현재 상황을 판단하여 투자하는 방식이다. 필자는 성장 가능성을 주로 보긴 하지만 가치 투자에 근거하여 일부 종목을 매매해보고자 자료를 수집하고 정리해봅니다. 투자에 참고하시길 바랍니다.

가치 투자 정의

기업 가치의 산출에는 여러 가지 방법이 있지만 금융감독원의 ‘유가증권 인수업무에 관한 규정’에서 제시하는 식을 살펴보면 아래와 같다

본질가치 = (자산가치 * 2+ 수익가치 * 3) /5

여기서 자산 가치는 재무상태상의 자본 총계를 의미하며 부채를 뺀 순 자산가치를 의미한다.

자산가치 = 자산총계 – 부채총계 = 순자산가치 = 청산가치

그리고 수익 가치는 기업활동으로 이익으로 발생 가능한 가치이다.

자산가치와 수익가치를 기업의 주식수로 나누면 각각 BPS(=자산가치)와 EPS(=수익가치)로 표현된다.

파이썬 활용 종목 검색

시가총액 2000억 이상이며 1/PER(=기대수익률) > 0.12 이상 그리고 PBR < 1.0 이하이면서 자산가치가 현재 주가보다 높은 종목(가치투자에 따른 상승 기대 종목)을 검색하기 위해 파이썬 코드로 검색해보았다.

대신증권 API 활용, Python 3.6.1(x32)

# find valuable items
import win32com.client

# declare Cybosplus API
cpStockCode = win32com.client.Dispatch("CpUtil.CpStockCode")
cpStockMst = win32com.client.Dispatch("dscbo1.StockMst") # not CpDib.StockMst

# total num of stock items
itNum = cpStockCode.GetCount()
print(itNum)

for it in range(0, itNum):

   # get item info
   itCode = cpStockCode.GetData(0, it)
   itName = cpStockCode.GetData(1, it)

   # request info
   cpStockMst.SetInputValue(0, itCode)
   cpStockMst.BlockRequest()

   # get header values
   managedStatus = chr(cpStockMst.GetHeaderValue(66)) # 관리구분
   alarmInvestStatus = chr(cpStockMst.GetHeaderValue(67)) # 투자 경고 구분
   tradeStopStatus = chr(cpStockMst.GetHeaderValue(68)) # 거래 정지 구분
   unfaithfulNoticeStatus = chr(cpStockMst.GetHeaderValue(69)) # 불성실 공시 구분
   exchangeType = chr(cpStockMst.GetHeaderValue(45)) # 소속구분 '1' : 거래소, '5': 코스닥

   # filtering items
   if managedStatus == 'N' and alarmInvestStatus == '1' and 
         tradeStopStatus == 'N' and unfaithfulNoticeStatus == '0':

      # only to consider KOSPI or KOSDAQ
      if exchangeType == '1' or exchangeType == '5':

         currentPrice = int(cpStockMst.GetHeaderValue(11)) # current price (현재가)
         stockNum = int(cpStockMst.GetHeaderValue(31)) # 발행주식수
         marketCap = currentPrice * stockNum
         EPS = float(cpStockMst.GetHeaderValue(20)) # EPS
         BPS = float(cpStockMst.GetHeaderValue(70)) # BPS
         PER = float(cpStockMst.GetHeaderValue(28)) # PER

         if BPS != 0:
            PBR = float(currentPrice/BPS) # PBR calculation
         else:
            PBR = float('inf')

         # calc essencial value (본질 가치)
         assetValue = BPS
         profitValue = EPS / 0.01 # 10% 할인률 적용
         eValue = (assetValue * 2 + profitValue * 3) / 5

         if marketCap > 200000000000 and PER != 0 and 1 / PER > 0.12 and PBR < 1.0:
            if eValue > currentPrice:
               print(itName + "<" + itCode + ">")

검색 결과 (2017년 8월 13일)

대림산업<A000210>
한국타이어월드와이드<A000240>
기아차<A000270>
대원강업<A000430>
동아쏘시오홀딩스<A000640>
강남제비스코<A000860>
한화<A000880>
대한제분<A001130>
유진투자증권<A001200>
부국증권<A001270>
종근당홀딩스<A001630>
신영증권<A001720>
오리온홀딩스<A001800>
삼호<A001880>
KISCO홀딩스<A001940>
한국유리<A002000>
아세아<A002030>
쌍용양회<A003410>
대신증권<A003540>
현대제철<A004020>
세방<A004360>
대림씨엔에스<A004440>
현대차<A005380>
넥센<A005720>
풍산홀딩스<A005810>
에스엘<A005850>
동아타이어<A007340>
서연<A007860>
현대모비스<A012330>
동원개발<A013120>
유니드<A014830>
부산가스<A015350>
예스코<A015360>
한국전력<A015760>
SK가스<A018670>
유진기업<A023410>
기업은행<A024110>
원익홀딩스<A030530>
교보증권<A030610>
LG디스플레이<A034220>
평화정공<A043370>
세아홀딩스<A058650>
세이브존I&C<A067830>
현대백화점<A069960>
GS<A078930>
휠라코리아<A081660>
대한제강<A084010>
한화생명<A088350>
현대EP<A089470>
SK이노베이션<A096770>
메리츠금융지주<A138040>
BNK금융지주<A138930>
DGB금융지주<A139130>
JB금융지주<A175330>
아세아시멘트<A183190>
광주은행<A192530>
서연이화<A200880>
코라오홀딩스<A900140>
GRT<A900290>

참조

 

R 함수로 코스닥 데이터 가져오기

R은 통계 관련 대표적인 오프 소스 도구이지만 머신러닝에서도 최근 많이 사용된다. 머신러닝이라고 하면 데이터를 다루는 기법(또는 분야 학문)다. 필자가 관심있어 하는 주식도 마찬가지로 데이터로 구성된 분야이기에 머신러닝 적용이 가능하다. 예로 예측하는 것도 주식의 과거 데이터를 이용해서 예측할 수 있겠다.

그러므로 R를 이용해서 어쩌면 주식 관련 처리를 하고 싶은 분들도 있을 것이다. 기본적인 통계뿐 아니라 머신러닝에도 적합한 도구이기 때문이다. 사실은 필자도 그 중 한명이기에 R를  활용하여 주식 관련 데이터를 처리하고 싶어 이것저것 찾아보았다. 역시나 R에서 주식 관련 패키지들이 존재한다.

quantmod, tseries 그리고 fImport와 같은 것들이 있다. 하지만 아쉽게도 국내 KOSDAQ 데이터를 가져올 수 있는 패키지는 없었다.

그렇지만 우연히 국내 사이트(http://blog.naver.com/lisist/220417131329)에서 R을 활용하여 코스닥 (KOSDAQ) 데이터를 가져오는 법을 확인할 수 있었다. 그나마 다행이었다. 다양한 패키지에 속해있는 함수들은 사용할 수 없겠지만 그래도 데이터를 가져올 수 있었다. 그 버전을 보완해서 약간의 locale 문제도 반영한 R 함수를 github에 올려놓았다. 참고하시길 바란다

https://github.com/lifegear/get-Korean-stocks-by-R

R 소스를 다운로드받고 우선  XML 패키지를 설치해야 한다. (필자의 경우 R Studio 활용)

> install.packages("xml")

그리고 나서 XML 패키지를 불러온다

> library(xml)

그런 다음 g.hist2 라는 함수를 아래와 같이 호출한다.

> output <- g.hist2("KOSDAQ:035720", "2017-01-01", "2017-05-01")
> output
 Date Open High Low Close Volume
1 2017-04-28 14050 14050 13600 13650 129595
2 2017-04-27 14000 14150 13900 13900 66842
3 2017-04-26 14000 14250 13750 14000 115246
4 2017-04-25 13900 14150 13850 13900 98483
5 2017-04-24 14250 14300 13850 13900 118903
6 2017-04-21 14350 14500 14000 14250 136055
7 2017-04-20 14650 14850 14150 14250 250277
8 2017-04-19 13600 15700 13600 14700 1282552
9 2017-04-18 13600 13600 13300 13550 151580
10 2017-04-17 13800 13850 13450 13500 175651
11 2017-04-14 14200 14200 13600 13750 155652
...

아래는 output 데이터를 활용해서 출력한 주식 차트 그래프이다

> plot(output$Date, output$Close, type="l")
chart

아래는 R 함수 소스이다 (github url 바로가기)

library(XML)

g.hist2 <- function(ticker, start_date, end_date){

 #ticker set & modify
 ticker_m <- gsub(":","%3A",ticker)

 #set locale
 lct<-Sys.getlocale("LC_TIME")
 Sys.setlocale("LC_TIME", "C")

 #set date
 start_date = as.Date(start_date)
 end_date = as.Date(end_date)

 #format date for calling google finance url
 fmtstr <- "%b+%d%%2C+%Y" #ex) Jan+01,+2017
 start_date_fmt <- as.character(format(start_date, fmtstr))
 end_date_fmt <- as.character(format(end_date, fmtstr))

 #set URL
 url = 'http://www.google.com/finance/historical?q='
 url = paste(url,ticker_m,'&startdate=',start_date_fmt,'&enddate=',end_date_fmt,'&num=200',sep='')

 #downloading data from the Google Finance
 output <- as.data.frame(readHTMLTable(url,stringsAsFactors=FALSE)[4])

 output[, 1] <- as.Date(output[, 1], format="%b %d, %Y")
 names(output) <- c("Date", "Open","High","Low","Close","Volume")

 for (k in 2:6){
 output[, k] <- as.numeric(gsub(",", "", output[, k]))
 }

 #### Checking whether all data are retrieved or not ####
 end <- as.Date(output[length(output[, 1]), 1])
 ll <- 200 - length(output[, 1])
 if ((end-ll)<start_date) end <- start_date
 if (end > start_date) {
 output <- rbind(output, g.hist2(ticker,start_date,as.character(end-1)))
 }

 return(output)
}

터틀 트레이딩

1980년 대 초 시카고 선물 시장에서 군림하던 트레이더 리처드 데니스가 트레이더 그룹을 강화하기 위해 13명의 상품 선물 트레이더를 모집한 적이 있다. 트레이더는 그 전까지는 재야의 고수처럼 그들만의 공식을 비밀로 하고 해서 드러나지 않은 직업이었기에 그 당시 트레이더의 모집은 놀라운 시도이었다. 중요한 사실은 그 곳에서 훈련받은 트레이더는 단지 2주간의 훈련으로 트레이더로써 업무를 시작했다는 것이고 더군다나 향후 실력있는 트레이더들로 성장했다는 것이다. 짧은 기간의 교육으로도 가능한 이유는 복잡한 시장원리나 경험보다는 단순한 규칙 기반의 트레이더 매매 방식을 적용했기 때문이다. 더 놀라운 사실은 그러한 규칙을 기반으로 한 트레이딩에 있어 많케는 년 150%에 달라는 엄청난 수익이 가능했다는 점이다. 터틀(시스템)이라 불리는 그들만의 공식을 정리해 본다.

시장(종목) 진입/이탈 규칙

4주간 또는 11주간의 신고점 내지는 신저점

매우 단순한 규칙이지만 추세 매매 방식이다. 모집된 트레이더들은 추세 매매의 규칙을 기반으로 트레이딩을 했던 것이다. 물론 약간의 개인적인 응용도 했을 것으로 생각된다.

손절매 규칙

2N 이하로 내려갈 경우

(n을 구하고 n의 이동평균을 구한 후의 오늘의 N값을 의미함. 즉, n은 오늘 고가와 저가와의 차이, 어제 종가와 오늘 고가와의 차이, 어제 종가와 오늘 저가의 차이 중 최대 절대값을 의미함. 가령 각각의 경우 60, -50, -100이며 n은 100라 정의. n을 오늘로부터 지난 15일 동안의 매일 n을 구한 후, 그 합을 15로 나누면 그것이 오늘의 변동성을 의미하는 N을 구할 수 있음)

피라미드 쌓기 규칙

어떤 종목(선물, 주식)을 신고점 돌파에 맞춰 매수를 했는데 그 가격이 계속 상승한다면 매입 주식 수를 더 늘리는 것이다. 정확히는 주가가 1N 상승할 때마다 투자를 늘려갔다.

유닛(리스트 관리) 규칙

터틀은 각각의 트레이딩에 대해 리스트 관리차원에서 최대 손실이 전체 투자금 대비 2퍼센트를 초과하지 않도록 트레이딩하였다. 2%의 유닛이라고 했다. 예로 총 100만원의 투자금이 있다고 하자. 100만원의 2%는 2만원이다. 최대 손실 가능성이 2만원이라는 의미이다. 금일 A 종목에 있어 N이 1500이라고 한다면 손절매 가격은 2N이기에 3000이라 할 수 있다. 따라서 20000(최대손실금)/3000(2N)=6 이므로 리스크 관리 차원에서 최대 6주(계약) 까지만 매수(시장진입)하라는 규칙이다.

터틀 트레이딩이 흥미로운 점은 의외로 단순하다는 점이다. 따라서 기계적으로 시스템 트레이딩하기에도 쉽다는 것이다. 조만간 실험적으로 실제 시스템 트레이딩을 구현해 검증을 하고 싶기도 하다.

참조

  • (월가를 긴장시킨 14일간의 수업) 터틀 트레이딩, 마이클 코벨 지음, 정명수 옮김, 위즈덤 하우스 출판