# 預測2330漲跌(lstm)並做出策略再進行回測 ###### tags: `tensorflow.keras` ## 抓資料 ```python= from webdriver_manager.chrome import ChromeDriverManager from selenium import webdriver from selenium.webdriver.chrome.options import Options import time from bs4 import BeautifulSoup import requests import pandas options = Options() options.add_argument("--disable-notifications") driver = webdriver.Chrome(ChromeDriverManager().install())#打開chrome瀏覽器 driver.get("https://hk.finance.yahoo.com/quote/2330.TW/history?period1=1332547200&period2=1648080000&interval=1d&filter=history&frequency=1d&includeAdjustedClose=true") for x in range(1,50):#將網頁往下滑50次 載入更多數據 height = driver.execute_script("return document.documentElement.scrollHeight") driver.execute_script("window.scrollTo(0, " + str(height) + ");") time.sleep(0.1) ``` ```python= soup = BeautifulSoup(driver.page_source) a = pandas.read_html(soup.prettify()) a[0] ``` ![](https://i.imgur.com/nkW474K.png) ```python= opening = [] hight = [] low = [] close = [] volume = [] date = [] for i in range(len(a[0])): if '股' not in a[0]['開市'][i] : if '-' not in a[0]['成交量'][i] : if '價' not in a[0]['開市'][i]: opening.append(float(a[0]['開市'][i])) hight.append(float(a[0]['最高'][i])) low.append(float(a[0]['最低'][i])) close.append(float(a[0]['收市*'][i])) volume.append(float(a[0]['成交量'][i])) date.append(a[0]['日期'][i]) print(i) date = list(reversed(date)) opening = list(reversed(opening)) hight = list(reversed(hight)) low = list(reversed(low)) close = list(reversed(close)) volume = list(reversed(volume)) ``` ```python= import re dat = [] for i in range(len(date)): a = re.sub('年','-',date[i]) b = re.sub('月','-',a) c = re.sub('日','-',b) dat.append(c) ``` ```python= full = pandas.DataFrame(( zip(opening,hight,low,close,volume)),columns = ['open','hight','low','close','volume']) full['updown'] = 0 for i in range(len(full)-1): if float(full['close'][i]) > float(full['open'][i+1]): full['updown'][i] = 1#1為長0為跌 ``` ![](https://i.imgur.com/1wcyswK.png) ## 正規畫 ```python= train = full[1:] test = full[1:] from sklearn.preprocessing import MinMaxScaler scaler = MinMaxScaler() train = pandas.DataFrame(scaler.fit_transform(train), columns=full.columns) test = pandas.DataFrame(scaler.fit_transform(test), columns=full.columns) ``` ## 抓取用來預測的資料 及 預測值 ```python= import tqdm n = 30 #用過去30天 x 預測 y資料 feature_names = list(train.drop('updown', axis=1).columns) X = [] y = [] indexes = [] norm_data_x = train[feature_names] for i in tqdm.tqdm_notebook(range(0,len(train)-n)): X.append(norm_data_x.iloc[i:i+n]. values) #iloc[n,m]是取n,m那格數字 iloc[n:m]是取n*m那些數字 y.append(train['updown'].iloc[i+n-1]) #現有資料+30天的Y indexes.append(train.index[i+n-1]) #Y的日期 ``` ## 建模 lstm(多用於預測) ```python= import keras from keras.models import Sequential from keras.layers import Dense,LSTM import numpy n_steps = 30 #前幾天的資料 n_features = 5#用幾個欄位 model = Sequential() model.add(LSTM(50,activation='relu', return_sequences=False, input_shape = (n_steps, n_features))) #model.add(神經元數量,激活函數,輸出是否為時間序列 如果輸出為兩個以上值 就是序列 要寫True,input_shape(幾步,幾個特徵)) model.add(Dense(1)) model.compile(optimizer = 'adam', loss = 'mse' , metrics=['mse','mape']) ``` ## 訓練 ```python= history = model.fit(numpy.array(X),numpy.array(y),batch_size = 100,epochs = 30) ``` ``` Epoch 1/30 24/24 [==============================] - 1s 12ms/step - loss: 0.3778 - mse: 0.3778 - mape: 93689392.0000 Epoch 2/30 24/24 [==============================] - 0s 12ms/step - loss: 0.2747 - mse: 0.2747 - mape: 197704432.0000 Epoch 3/30 24/24 [==============================] - 0s 12ms/step - loss: 0.2490 - mse: 0.2490 - mape: 248887376.0000 Epoch 4/30 24/24 [==============================] - 0s 12ms/step - loss: 0.2493 - mse: 0.2493 - mape: 240265088.0000 Epoch 5/30 24/24 [==============================] - 0s 13ms/step - loss: 0.2476 - mse: 0.2476 - mape: 236481664.0000 Epoch 6/30 24/24 [==============================] - 0s 12ms/step - loss: 0.2481 - mse: 0.2481 - mape: 244062752.0000 Epoch 7/30 24/24 [==============================] - 0s 13ms/step - loss: 0.2475 - mse: 0.2475 - mape: 244211888.0000 Epoch 8/30 24/24 [==============================] - 0s 12ms/step - loss: 0.2469 - mse: 0.2469 - mape: 244918192.0000 Epoch 9/30 24/24 [==============================] - 0s 12ms/step - loss: 0.2475 - mse: 0.2475 - mape: 243948992.0000 Epoch 10/30 24/24 [==============================] - 0s 13ms/step - loss: 0.2475 - mse: 0.2475 - mape: 249503232.0000 Epoch 11/30 24/24 [==============================] - 0s 12ms/step - loss: 0.2476 - mse: 0.2476 - mape: 237542128.0000 Epoch 12/30 24/24 [==============================] - 0s 12ms/step - loss: 0.2470 - mse: 0.2470 - mape: 245051488.0000 Epoch 13/30 24/24 [==============================] - 0s 13ms/step - loss: 0.2469 - mse: 0.2469 - mape: 244758416.0000 Epoch 14/30 24/24 [==============================] - 0s 13ms/step - loss: 0.2469 - mse: 0.2469 - mape: 247838048.0000 Epoch 15/30 24/24 [==============================] - 0s 12ms/step - loss: 0.2473 - mse: 0.2473 - mape: 243551968.0000 Epoch 16/30 24/24 [==============================] - 0s 12ms/step - loss: 0.2468 - mse: 0.2468 - mape: 242248672.0000 Epoch 17/30 24/24 [==============================] - 0s 13ms/step - loss: 0.2466 - mse: 0.2466 - mape: 241368496.0000 Epoch 18/30 24/24 [==============================] - 0s 13ms/step - loss: 0.2466 - mse: 0.2466 - mape: 243799472.0000 Epoch 19/30 24/24 [==============================] - 0s 12ms/step - loss: 0.2464 - mse: 0.2464 - mape: 248815344.0000 Epoch 20/30 24/24 [==============================] - 0s 13ms/step - loss: 0.2470 - mse: 0.2470 - mape: 248027072.0000 Epoch 21/30 24/24 [==============================] - 0s 12ms/step - loss: 0.2465 - mse: 0.2465 - mape: 243964112.0000 Epoch 22/30 24/24 [==============================] - 0s 12ms/step - loss: 0.2474 - mse: 0.2474 - mape: 245234512.0000 Epoch 23/30 24/24 [==============================] - 0s 13ms/step - loss: 0.2464 - mse: 0.2464 - mape: 245181232.0000 Epoch 24/30 24/24 [==============================] - 0s 12ms/step - loss: 0.2463 - mse: 0.2463 - mape: 249691536.0000 Epoch 25/30 24/24 [==============================] - 0s 13ms/step - loss: 0.2459 - mse: 0.2459 - mape: 248387216.0000 Epoch 26/30 24/24 [==============================] - 0s 12ms/step - loss: 0.2470 - mse: 0.2470 - mape: 242287008.0000 Epoch 27/30 24/24 [==============================] - 0s 13ms/step - loss: 0.2461 - mse: 0.2461 - mape: 242720112.0000 Epoch 28/30 24/24 [==============================] - 0s 12ms/step - loss: 0.2460 - mse: 0.2460 - mape: 247012384.0000 Epoch 29/30 24/24 [==============================] - 0s 12ms/step - loss: 0.2458 - mse: 0.2458 - mape: 247025296.0000 Epoch 30/30 24/24 [==============================] - 0s 12ms/step - loss: 0.2458 - mse: 0.2458 - mape: 239765280.0000 ``` ## 預測 ```python= predictions= model.predict(numpy.array(X))#丟到模型做預測 predictions = pandas.DataFrame(predictions).rename(columns={0:'預測值'})#轉成DataFrame格式 predictions#x的預測值 y_test = pandas.DataFrame(numpy.array(y)).rename(columns={0 : "實際值"}) final = pandas.concat([predictions,y_test],axis = 1)#合併 final["mae"] = abs(final["預測值"]-final["實際值"]) final ``` ![](https://i.imgur.com/9mtGEJD.png) ```python= for i in range(len(final['預測值'])): if float(final['預測值'][i]) < 0.5: final['預測值'][i] = 0 if float(final['預測值'][i]) >= 0.5: final['預測值'][i] = 1 a = 0 for i in range(len(final['預測值'])): if final['預測值'][i] == final['實際值'][i] : a+=1 print(a/len(final['預測值'])) ``` 輸出結果 0.6059050064184852 # 回測 當預測為長買進 預測為跌則賣出 ## ```python= df = {'Date':dat, 'Volume':volume, 'Open':opening, 'High':hight, 'Low': low, 'Close':close, } df = pandas.DataFrame(df) df.set_index('Date', inplace = True) df.drop(df.head(100).index,inplace=True) df.drop(df.tail(1).index,inplace = True) df.index = pandas.to_datetime(df.index) ``` ## 交易策略 ```python= from backtesting import Backtest, Strategy from backtesting.test import SMA #從test子模組引入繪製均線功能 class Strategy(Strategy): #交易策略命名為SmaClass,使用backtesting.py的Strategy功能 def init(self): price = self.data.Close self.dataclose = self.data.Close self.dataopen = self.data.Open self.datahigh = self.data.High self.datalow = self.data.Low self.datavolume = self.data.Volume def next(self): close = [] opening = [] high = [] low = [] volume = [] for i in range(0,-30,-1): close.append(self.dataclose[i]) opening.append(self.dataopen[i]) high.append(self.datahigh[i]) low.append(self.datalow[i]) volume.append(self.datavolume[i]) o = pandas.DataFrame(( zip(opening,high,low,close,volume)),columns = ['open','hight','low','close','volume']) train = pandas.DataFrame(scaler.fit_transform(o)) feature_names = list(train) X = [] X.append(train[feature_names].values) predictions = model.predict(numpy.array(X)) if predictions >0.5: self.buy() if predictions <=0.5: self.sell() ``` ```python= test = Backtest(df, SmaCross, cash=600000, commission=.002) test.run() ``` ![](https://i.imgur.com/MloDuFh.png)