News & Events
주가 예측에 관한 글은 매우 많지만, 이 포스팅은 다른 글에서는 알려주지 않는 두 가지를 제공한다.
- 신뢰 구간을 사용한 손실 감소 및 이익 실현
- 이익을 추적하고 거래 전략을 시험하기 위한 alpaca 사용
두 가지 모두 차세대 머신 러닝 거래 알고리즘에 중요한 툴을 제공한다.
개념:
이 프로그램은 세 가지 주요 부분으로 구성된다.
- 데이터 셋업
데이터는 yfinance 라이브러리를 통해 매일 액세스되며, 데이터에는 자산의 시가, 고가, 저가, 종가가 포함된다. 데이터가 정규화된 다음 신경망에 맞게 재구성된다.
- 신경망
신경망은 피쳐를 추출하고 데이터 세트의 시간적 특징에 액세스할 수 있는 컨볼루션 LSTM 네트워크가 될 것이다. 일부 복잡한 패턴은 컨볼루션일 뿐만 아니라 시간 기반이기 때문에 이 네트워크는 데이터에 적합하다.
- 주문 작성
신경망은 시가과 종가를 예측할 것이다. 시가가 종가보다 크면 네트워크에서는 주식을 공매도한다. 종가가 시가보다 크면 네트워크가 주식을 사게 된다.
네트워크를 훈련시킨 후에, 필자는 네트워크의 손실을 계산하고 이 값을 신뢰 구간으로 사용하여 스탑 로스를 결정하고 이익을 취할 것이다. Alpaca API 액세스 요청으로 주문해보자.
키 컨셉을 잡은 상태에서 코드로 넘어가 보자.
코드:
Step 1| 필수 구성 요소:
from numpy import array
from numpy import hstack
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
from keras import callbacks
from sklearn.model_selection import train_test_split
from keras.layers import Flatten
from keras.layers import TimeDistributed
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D
from IPython.display import clear_output
import datetime
import statistics
import time
import os
import json
import yfinance as yf
from keras.models import model_from_json
import requests
from keras.models import load_model
from matplotlib import pyplot as plt
이 프로그램의 필수 구성 요소는 꽤 많다. 라이브러리를 통째로 들여와 공간을 차지하지 않기 위해 이렇게 펼쳐져 있다. IPython.display에서 clear_output을 가져오는 것은 Jupyter 노트북에만 이용된다. 스크립트를 사용하는 경우 임포트 할 필요는 없다.
Step 2| 데이터 액세스:
def data_setup(symbol,data_len,seq_len):
end = datetime.datetime.today().strftime(‘%Y-%m-%d’)
start = datetime.datetime.strptime(end, ‘%Y-%m-%d’) – datetime.timedelta(days=(data_len/0.463))
orig_dataset = yf.download(symbol,start,end)
close = orig_dataset[‘Close’].values
open_ = orig_dataset[‘Open’].values
high = orig_dataset[‘High’].values
low = orig_dataset[‘Low’].values
dataset,minmax = normalize_data(orig_dataset)
cols = dataset.columns.tolist()
data_seq = list()
for i in range(len(cols)):
if cols[i] < 4:
data_seq.append(dataset[cols[i]].values)
data_seq[i] = data_seq[i].reshape((len(data_seq[i]), 1))
data = hstack(data_seq)
n_steps = seq_len
X, y = split_sequences(data, n_steps)
n_features = X.shape[2]
n_seq = len(X)
n_steps = seq_len
print(X.shape)
X = X.reshape((n_seq,1, n_steps, n_features))
true_y = []
for i in range(len(y)):
true_y.append([y[i][0],y[i][1]])
return X,array(true_y),n_features,minmax,n_steps,close,open_,high,low
이 함수는 yfinance에서 데이터를 가져와 해당 섹션으로 분할한다. 또한 데이터를 다음과 같은 형태로 재구성한다.
(n_seq,1, n_steps, n_features)
컨볼루젼 LSTM 네트워크에 적합한 4차원 배열.
Step 3| 데이터 준비:
데이터 액세스는 당면 과제의 절반에 불과하다. 나머지는 데이터를 올바른 형식으로 배치하고 데이터를 훈련 및 테스트 데이터셋으로 나누는 것이다.
def split_sequences(sequences, n_steps):
X, y = list(), list()
for i in range(len(sequences)):
end_ix = i + n_steps
if end_ix > len(sequences)-1:
break
seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix, :]
X.append(seq_x)
y.append(seq_y)
return array(X), array(y)
이 함수는 시퀀스를 n_step 크기의 청크로 분할하여 시퀀스를 시계열 데이터로 분할한다.
def normalize_data(dataset):
cols = dataset.columns.tolist()
col_name = [0]*len(cols)
for i in range(len(cols)):
col_name[i] = i
dataset.columns = col_name
dtypes = dataset.dtypes.tolist()
# orig_answers = dataset[attr_row_predict].values
minmax = list()
for column in dataset:
dataset = dataset.astype({column: ‘float32’})
for i in range(len(cols)):
col_values = dataset[col_name[i]]
value_min = min(col_values)
value_max = max(col_values)
minmax.append([value_min, value_max])
for column in dataset:
values = dataset[column].values
for i in range(len(values)):
values[i] = (values[i] – minmax[column][0]) / (minmax[column][1] – minmax[column][0])
dataset[column] = values
dataset[column] = values
return dataset,minmax
이 함수는 모든 데이터를 0~1의 값으로 변경한다. 이는 많은 주식이 급등하거나 급락한 것과 같다. 정규화를 진행하지 않으면 신경망은 더 높은 값을 가진 데이터 포인트를 통해 학습하게 된다. 이는 사각지대를 만들어 예측에 영향을 미칠 수 있다. 정규화는 다음과 같이 이루어진다.
value = (value – minimum) / maximum
여기서 최소값과 최대값은 피쳐의 최소값과 최대값이다.
def enviroment_setup(X,y):
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33)
return X_train, X_test, y_train, y_test
이 함수는 sklearn의 train_test_split 함수를 사용하여 데이터를 섞고 훈련 데이터 세트와 테스트 데이터 세트로 나눈다.
Step 4| 신경망 제작:
def initialize_network(n_steps,n_features,optimizer):
model = Sequential()
model.add(TimeDistributed(Conv1D(filters=64, kernel_size=1, activation=’relu’), input_shape=(None, n_steps, n_features)))
model.add(TimeDistributed(MaxPooling1D(pool_size=2)))
model.add(TimeDistributed(Flatten()))
model.add(LSTM(50, activation=’relu’))
model.add(Dense(2))
model.compile(optimizer=optimizer, loss=’mse’)
return model
이는 컨볼루션 LSTM 네트워크의 기본 아키텍처이다. 이 네트워크에서 가장 효과적인 최적화 도구는 Adam이다.
Step 5| 훈련 신경망:
def train_model(X_train,y_train,model,epochs):
dirx = ‘something directory’
os.chdir(dirx)
h5=’Stocks’+’_best_model’+’.h5′
checkpoint = callbacks.ModelCheckpoint(h5, monitor=’val_loss’, verbose=0, save_best_only=True, save_weights_only=True, mode=’auto’, period=1)
earlystop = callbacks.EarlyStopping(monitor=’val_loss’, min_delta=0, patience=epochs * 1/4, verbose=0, mode=’auto’, baseline=None, restore_best_weights=True)
callback = [earlystop,checkpoint]
json = ‘Stocks’+’_best_model’+’.json’
model_json = model.to_json()
with open(json, “w”) as json_file:
json_file.write(model_json)
history = model.fit(X_train, y_train, epochs=epochs, batch_size=len(X_train)//4, verbose=2,validation_split = 0.3, callbacks = callback)
return history
훈련 기능 때문에 필자는 범죄적으로 사용되지 않는 모델 체크포인트 콜백을 사용하여 모델의 최고 가중치를 저장했다. dirx 변수를 모델을 저장하는 위치로 변경한다.
Step 6| 평가 및 예측:
def load_keras_model(dataset,model,loss,optimizer):
dirx = ‘something directory’
os.chdir(dirx)
json_file = open(dataset+’_best_model’+’.json’, ‘r’)
loaded_model_json = json_file.read()
json_file.close()
model = model_from_json(loaded_model_json)
model.compile(optimizer=optimizer, loss=loss, metrics = None)
model.load_weights(dataset+’_best_model’+’.h5′)
return model
def evaluation(exe_time,X_test, y_test,X_train, y_train,history,model,optimizer,loss):
model = load_keras_model(‘Stocks’,model,loss,optimizer)
test_loss = model.evaluate(X_test, y_test, verbose=0)
train_loss = model.evaluate(X_train, y_train, verbose=0)
eval_test_loss = round(100-(test_loss*100),1)
eval_train_loss = round(100-(train_loss*100),1)
eval_average_loss = round((eval_test_loss + eval_train_loss)/2,1)
print(“— Training Report —“)
plot_loss(history)
print(‘Execution time: ‘,round(exe_time,2),’s’)
print(‘Testing Accuracy:’,eval_test_loss,’%’)
print(‘Training Accuracy:’,eval_train_loss,’%’)
print(‘Average Network Accuracy:’,eval_average_loss,’%’)
return model,eval_test_loss
최적의 가중치를 저장한 후 모형을 다시 로드하여 최적의 가중치를 사용하고 있는지 확인한다. 그런 다음 프로그램은 이전에 보지 못한 데이터를 기반으로 프로그램을 평가한다. 그런 다음 일련의 변수를 프린트하여 네트워크 훈련에 대한 포괄적인 인사이트를 제공한다.
def market_predict(model,minmax,seq_len,n_features,n_steps,data,test_loss):
pred_data = data[-1].reshape((len(data[-1]),1, n_steps, n_features))
pred = model.predict(pred_data)[0]
appro_loss = list()
for i in range(len(pred)):
pred[i] = pred[i] * (minmax[i][1] – minmax[i][0]) + minmax[i][0]
appro_loss.append(((100-test_loss)/100) * (minmax[i][1] – minmax[i][0]))
return pred,appro_loss
이것은 프로그램의 예측을 만드는 함수이다. 우리는 USD 단위로 값을 얻으려면 정규화 함수의 역수를 수행해야 한다.
Step 7| 주문 작성:
BASE_URL = ‘https://paper-api.alpaca.markets’
API_KEY = ‘XXXXXXXX’
SECRET_KEY = ‘XXXXXXXX’
ORDERS_URL = ‘{}/v2/orders’.format(BASE_URL)
HEADERS = {‘APCA-API-KEY-ID’:API_KEY,’APCA-API-SECRET-KEY’:SECRET_KEY}
이러한 매개 변수와 엔드포인트는 alpaca 주문을 만드는 데 사용된다. 여기서 자신만의 API 키와 비밀 키를 얻을 수 있다.
def create_order(pred_price,company,test_loss,appro_loss):
open_price,close_price = pred_price[0],pred_price[1]
if open_price > close_price:
side = ‘sell’
elif open_price < close_price:
side = ‘buy’
if side == ‘buy’:
order = {
‘symbol’:company,
‘qty’:round(20*(test_loss/100)),
‘type’:’stop_limit’,
‘time_in_force’:’day’,
‘side’: ‘buy’,
‘take_profit’: close_price + appro_loss,
‘stop_loss’: close_price – appro_loss
}
elif side == ‘sell’:
order = {
‘symbol’:company,
‘qty’:round(20*(test_loss/100)),
‘type’:’stop_limit’,
‘time_in_force’:’day’,
‘side’: ‘sell’,
‘take_profit’:close_price – appro_loss,
‘stop_loss’:close_price + appro_loss
}
r = requests.post(ORDERS_URL, json = order,headers = HEADERS)
print(r.content)
이 기능은 이익 및 스탑로스 아이디어를 적용한다.
종가 예상치에 의해서 종가가 변동하기 때문에 이익을 얻어 손실을 방지한다.
빨간색 영역의 경계와 중심 사이의 길이는 손실 값이다. 경계는 스탑로스로 작용하고 이익을 취한다. 이는 프로그램이 가격이 변동될 것으로 예측하는 값이기 때문이다.
맺음말:
머신 러닝과 주식 거래는 둘 다 복잡한 패턴에 대한 예측이기 때문에 밀접한 관련이 있다.
알고리즘 트레이딩에 대해서는 Alpaca API와 신뢰구간을 더 많은 사람들이 이용할 수 있었으면 좋겠다.
번역 – 핀인사이트 인턴연구원 강지윤(shety0427@gmail.com)
원문 보러가기>
https://medium.com/analytics-vidhya/using-deep-learning-to-create-a-stock-trading-bot-a96e6351d31c