รู้รึเปล่า? หุ้นตัวไหนน่าเสี่ยง น่าซื้อ… ก็สามารถใช้ Machine learning ทำนายได้นะ😉

Kulyarat L.
botnoi-classroom
Published in
5 min readSep 15, 2020

Objective:

บทความนี้เป็นส่วนหนึ่งของ Data Science Essential ของ Botnoi Classroom หัวข้อ Trend forecasting

จนถึงสัปดาห์นี้ เราก็เรียนรู้เรื่องการสร้างโมเดล เทรนโมเดล และ Predict กันไปแล้ว รวมทั้งเรียนรู้การทำ Pipeline ด้วย

ในสัปดาห์นี้เราได้เรียนเกี่ยวกับการใช้ Maching Learning เพื่อวิเคราะห์หุ้น ดังนั้น… งานในสัปดาห์นี้ของเราเองก็จะเป็นการวิเคราห์หุ้นเช่นกัน

และข้อมูลที่เราเลือกก็คือ…

วิเคราะห์หุ้น GC-F (บริษัท โกลบอล คอนเน็คชั่นส์ จำกัด )(มหาชน)นั่นเอง!!

ทีนี้เราก็มาเริ่มกันเลยดีกว่า

Step 0 เริ่มจาก import library ก่อนเหมือนเดิมเลยค่ะ

# pandas and numpy are used for data manipulationimport pandas as pdimport numpy as np# matplotlib and seaborn are used for plotting graphsimport matplotlib.pyplot as plt%matplotlib inlineplt.style.use('seaborn-darkgrid')
# fix_yahoo_finance is used to fetch dataimport yfinance as yf

Step 1 Get data

# Read datadef getPrice(name,YearMonthDateStart,YearMonthDateEnd,auto_adjust=True):Dataframe = yf.download(name, YearMonthDateStart, YearMonthDateEnd, auto_adjust)return Dataframe

อันนี้เราสร้าง method ไว้อ่านข้อมูลของ stock ที่เราเลือก โดยใส่ชื่อ วันที่เริ่มต้น วันที่สุดท้ายลงไป แล้วไปดาวน์โหลดมาจาก yfinance วิธีเอาmethod ไปใช้ก้ตามโค้ดด้านล่างเลย 👇

NameOfStock = 'GC=F'YearMonthDateStart ='2006-01-01'YearMonthDateEnd = '2016-12-31'GoldTest = getPrice(NameOfStock, '2006-01-01', '2016-12-31',auto_adjust=True)GoldTest
แล้วเราก็จะได้ตารางออกมาแบบนี้

เราก็จะรู้ราคาเปิด ราคาปิด ราคาสูง-ต่ำ ของแต่ละวัน ใน period ที่เราเลือกแล้ว

จากรูปก็จะเห็เทรนด์ราคาคร่าว ๆ ของหุ้นตัวนี้

เท่านี้เราก็ได้ข้อมูลมาเรียบร้อย ถ้างั้นก็… ไปขั้นต่อไปกันเลย!!

Step 2 Data Cleansing and Transformation

อันดับแรกมาเช็ค Data type กับ missing value กันก่อน

ทีนี้เราก็จะมา drop คอลลัมน์ที่เราไม่ใช้ทิ้งไป

def Drop_DivSplit(dataframe):dataframe.drop('Dividends', axis=1, inplace=True)dataframe.drop('Stock Splits', axis=1, inplace=True)dataframe.drop('Adj Close', axis=1, inplace=True)return dataframe
ก็จะได้มาแบบนี้ ตอนนี้ก็ไม่มีสามคอลลัมน์ที่เราไม่ใช้แล้ว

2.1 Get Features

# Create get_X_y function to get dependent (y) and independent (X) variablesdef get_X_y(dataframe):# Extract time series variableX = dataframe.reset_index()[['Date']]X['Year'] = [X.iloc[i, 0].year for i in range(len(X))]X['Month'] = [X.iloc[i, 0].month for i in range(len(X))]X['Day'] = [X.iloc[i, 0].day for i in range(len(X))]X['EnglishDay'] = X.Date.dt.strftime('%a')# Set categorical variableX = X.astype({'Month':'category','Day':'category','EnglishDay':'category'}).set_index('Date')# Get OHLCX = X.merge(dataframe, left_index=True, right_index=True)y = dataframe[['NextDayClose']].copy()return X,y

จากโค้ดนี้ก็คือเรากำลังจะทำตัว features เป็น time series ดังนั้น เราก็จะทำให้วันที่กลายเป็น คอลลัมน์ขึ้นมาก่อน

ทีนี้เราก็จะทำให้ Month, Day, EnglishDay เป็น category เพื่อที่เราจะสามารถจับ Trend ของข้อมูลได้ ตัวอย่างเช่น ทุกวันศุกร์ บีทีเอสจะมีคนใช้เยอะเป็นพิเศษ

เสร็จแล้วเราก็เอา original dataframe มา merge กับตัวที่มี features

ก็จะออกมาแบบนี้
# Create feature_engineer function to extract new featuresdef feature_engineer(dataframe, feat_eng=False):#Looking for 90 dayif feat_eng:# Create a feature from next day Closedataframe[f'NextDayClose'] = dataframe[['Close']].shift(periods=-1,fill_value=0)for i in range(1,91):# Create a feature from previous day HIGHdataframe[f'PreviousDayHigh{i}'] =  dataframe[['High']].shift(periods=i,fill_value=0)# Create a feature from previous day LOWdataframe[f'PreviousDayLow{i}'] = dataframe[['Low']].shift(periods=i, fill_value=0)# Create a feature from previous day Opendataframe[f'PreviousDayOpen{i}'] = dataframe[['Open']].shift(periods=i,fill_value=0)# Create a feature from previous day Closedataframe[f'PreviousDayClose{i}'] = dataframe[['Close']].shift(periods=i,fill_value=0) 
dataframe = dataframe[dataframe.PreviousDayHigh90!= 0 ]
dataframe = dataframe[:-1]return dataframe

เสร็จแล้วเราก็จะสร้าง features จากอย่างอื่นนอกจาก time กันบ้าง โดยจากโค้ดนี้เราก็จะสร้าง features จาก ราคาเปิด-ปิด สูง-ต่ำ ของวันก่อนหน้า วันที่กำลังวิเคราะห์อยู่ (เช่นถ้าวิเคราะห์ข้อมูลวันที่ 20 ก็ใช้ราคาวันที่ 19)

def feature_diff(dataframe):dataframe[f'NextDayClose'] = dataframe[f'NextDayClose']-dataframe['Close']for i in range(1,91):# Create a feature from previous day HIGHdataframe[f'PreviousDayHigh{i}'] =  dataframe[f'PreviousDayHigh{i}']- dataframe['High']# Create a feature from previous day LOWdataframe[f'PreviousDayLow{i}'] =  dataframe[f'PreviousDayLow{i}']- dataframe['Low']# Create a feature from previous day Opendataframe[f'PreviousDayOpen{i}'] =  dataframe[f'PreviousDayOpen{i}']- dataframe['Open']# Create a feature from previous day Opendataframe[f'PreviousDayClose{i}'] =  dataframe[f'PreviousDayClose{i}']- dataframe['Close']return dataframe

method นี้เป็นการหา diff ราคาของวันที่วิเคราะห์ กับวันก่อนหน้า แล้วไปทำเป็น features เช่นกัน

2.2 Get one-hot encoder

def one_hot_encoder(dataframe):# Select category columnscat_df = dataframe.select_dtypes(include=['category']).columns.to_list()
# Convert to one-hot dataframeone_hot_df = pd.get_dummies(dataframe, columns=cat_df)return one_hot_df

ก็คือเราจะมา encode ข้อมูลในคอลลัมน์ที่เป็น category ให้กลายเป็นตัวเลข 0,1 เพื่อที่จะสามารถเอาไปรันในโมเดลได้

หน้าตาประมาณนี้

2.3. Split train and test dataframe

# Create stock_train_test_split functiondef stock_train_test_split(dataframe, y, trainsize=0.8):import mathtrain_ratio = trainsizetrain_size = math.ceil(len(dataframe) * train_ratio)# Get train dataframeX_train = dataframe.iloc[:train_size, :]y_train = y.iloc[:train_size, :]# Get train dataframe# X_test = dataframe.iloc[train_size:, :]X_test = dataframe.copy()y_test = y.iloc[train_size:, :]# Drop label from independent variablecol_drop = ['OPEN', 'HIGH', 'LOW', 'CLOSE', 'VOLUME']X_train = X_train.drop(col_drop, axis=1)X_test = X_test.drop(col_drop, axis=1)return X_train, X_test, y_train, y_test

ในขั้นตอนนี้เราจะเริ่มมา split ข้อมูลเป็น train กับ test เพื่อที่จะเอาไปใช้ในการ fit model และ predict กันต่อไป

Step 3 Prediction model

3.1 Model

# Create model_fit function for setting prediction modeldef model_fit(X_train, y_train):
# import sci-kit learnfrom sklearn.linear_model import LinearRegression
# Model fitting with normalizationmodel = LinearRegression(normalize=True).fit(X_train, y_train)
return model

โค้ดนี้ก็คือเราทำการ fit model โดย normalize แบบ linear regression

3.2 Error

def rmse(y_test, y_hat):# Root mean squared errorfrom sklearn.metrics import mean_squared_errorfrom math import sqrty_hat_test = y_hat[-len(y_test):]rms = sqrt(mean_squared_error(y_test, y_hat_test))return print('RMS = {}'.format(rms))

เสร็จแล้วก็จะมาทำการหาค่า RMSE (Root Mean Square Error) ค่ายิ่งเข้าใกล้ 0 เท่าไหร่ แปลว่า predict แม่นยำมากขึ้นเท่านั้น

และนี่ก้คือค่า error ของโมเดลเรา

เป็นอันเสร็จสิ้นขั้นตอนของการทำโมเดล ทีนี้เพื่อให้เราสามารถดู result ได้ง่าย ๆ เราก้ไปที่ขั้นตอนถัดไปกัน

Step 4 Visualization

# Create visualize function to display resultdef visualize(X, y, y_hat, symbol, feat_eng=False):# Set figure sizeplt.figure(figsize=(10, 5))# Plot actual priceplt.plot(X.index, y, label='Actual Different Price')# Plot predicted priceplt.plot(X.index, y_hat, label='Predicted Different Price')# Set titleplt.title('Stock price: {} [{}]'.format(symbol, feat_eng) )# Set x-labelplt.xlabel('Period')# Set y-labelplt.ylabel('Price [THB]')# Plot gridplt.grid()# Display legendplt.legend()# Display graphplt.show()

ก็คือทำ visualization เพื่อเปรียบเทียบระหว่าง ราคาจริง กับ ราคาที่ model มัน predict มาได้

จากรูปจะเห็นว่าเส้นสีส้มซึ่งเป็นราคาที่ทำนายออกมา ทับเส้นสีฟ้าจนแทบมองไม่เห็น ซึ่งนั่นควรจะหมายความว่า ทำนายได้ใกล้เคียงราคาจริงมาก

และนั่นก็คือทั้งหมดของงานที่พวกเราทำค่า เย้!! 👏🏽👏🏽👏🏽

--

--