รู้รึเปล่า? หุ้นตัวไหนน่าเสี่ยง น่าซื้อ… ก็สามารถใช้ Machine learning ทำนายได้นะ😉
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 แม่นยำมากขึ้นเท่านั้น
เป็นอันเสร็จสิ้นขั้นตอนของการทำโมเดล ทีนี้เพื่อให้เราสามารถดู 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 มาได้
จากรูปจะเห็นว่าเส้นสีส้มซึ่งเป็นราคาที่ทำนายออกมา ทับเส้นสีฟ้าจนแทบมองไม่เห็น ซึ่งนั่นควรจะหมายความว่า ทำนายได้ใกล้เคียงราคาจริงมาก
และนั่นก็คือทั้งหมดของงานที่พวกเราทำค่า เย้!! 👏🏽👏🏽👏🏽