當AI遇上股票
台南一中AI選修教材 / 顏永進

Table of Contents

Hits

以下範例均假設於Google Colab上執行,若於本機IDE(如PyCharm、Spyder)上執行,則部份指令執行方式要略做修正。

1. CNN-1: 以AI預測股價-隔日漲跌

1.1. 安裝相關套件

1: pip install yfinance

1.2. 下載股價資訊

1: import yfinance as yf
2: 
3: df = yf.Ticker('2330.TW').history(period='10y')
4: print(type(df))

1.2.1. 查看下載的資料集

1: print(df[:5])

1.2.2. 取出需要的收盤價

從陣列中讀出收盤價

1: data = df.filter(['Close'])
2: print(data.head())

1.3. 觀察原始資料/日K圖

1: import matplotlib.pyplot as plt
2: plt.clf()
3: plt.plot(data.Close)
4: plt.show()

1.4. 將資料標準化

1: from sklearn.preprocessing import MinMaxScaler
2: scaler = MinMaxScaler(feature_range=(0, 1))
3: sc_data = scaler.fit_transform(data.values)
4: 
5: sc_data #變成numpy array

1.5. 建立、分割資料

1.5.1. 建立資料集及標籤

 1: import numpy as np
 2: 
 3: # 以前N天的股價來預測未來股價
 4: previousNDays = 10
 5: x_data, y_data = [], []
 6: for i in range(len(sc_data) - previousNDays):
 7:   x = sc_data[i:i+previousNDays]
 8:   y = sc_data[i+previousNDays]
 9:   x_data.append(x)
10:   y_data.append(y)
11: #因為等一下要送進tensorflow,所以先轉成numpy的陣列格式
12: x_data, y_data = np.array(x_data), np.array(y_data)
13: 
14: print(x_data.shape)
15: print(y_data.shape)

1.5.2. 分割訓練集與測試集

 1: ratio = 0.8
 2: train_size = round(len(x_data) * ratio)
 3: print(train_size)
 4: # 第0筆到第train_size-1筆的資料分割為訓練集
 5: x_train, y_train = x_data[:train_size], y_data[:train_size]
 6: # 第train_size筆到最後一筆的資料分割為測試集
 7: x_test, y_test = x_data[train_size:], y_data[train_size:]
 8: #from sklearn.model_selection import train_test_split
 9: #x_train, x_test, y_train, y_test = train_test_split(x_data, y_data, test_size=0.2)
10: print(x_train.shape)
11: print(y_train.shape)
12: print(x_test.shape)
13: print(y_test.shape)

1.6. 建立、編譯、訓練模型

1.6.1. 建立模型

 1: # CNN模型
 2: import tensorflow as tf
 3: from tensorflow.keras import layers
 4: 
 5: #建構CNN模型
 6: model = tf.keras.Sequential([
 7:     layers.Input(shape=(previousNDays, 1, 1)),
 8:     #卷積層:用 3 天的窗格掃描股價模式
 9:     layers.Conv2D(filters=512, kernel_size=(3, 1), activation='relu'),
10:     #攤平
11:     layers.Flatten(),
12:     #全連接層/輸出層(回歸問題不加激活函數)
13:     layers.Dense(1)
14: ])
1: model.summary()

1.6.2. 編譯模型

1: model.compile(loss='mse', optimizer='adam', metrics=['mae'])

1.6.3. 訓練模型

1: model.fit(x_train, y_train,
2:           validation_split=0.2,
3:           batch_size=200, epochs=20)

1.7. 性能測試

1.7.1. loss

1: score = model.evaluate(x_test, y_test)
2: print('loss:', score[0])
3: print('mae:', score[1])

1.7.2. predict

1: predict = model.predict(x_test)
2: predict = scaler.inverse_transform(predict)
3: predict = np.reshape(predict, (predict.size,))
4: ans = scaler.inverse_transform(y_test)
5: ans = np.reshape(ans, (ans.size,))
6: print(predict[:3])
7: print(ans[:3])

1.7.3. plot

1: plt.clf()
2: plt.plot(predict, label='Predicted')
3: plt.plot(ans, label='Actual')
4: plt.legend()
5: plt.show()

1.8. 能怎麼胡搞

  • 多讀些原始資料
  • 用更多特徵值來預測
  • 用更多/更少天數來預測
  • 變更模型架構
  • 變更訓練集:測試集比例
  • 增加epoch

2. CNN-2: 以AI預測股價-隔日漲跌

2.1. 安裝相關套件

1: pip install yfinance

2.2. 下載股價資訊

1: import yfinance as yf
2: 
3: df = yf.Ticker('2330.TW').history(period='10y')
4: print(type(df))

2.2.1. 查看下載的資料集

1: print(df[:5])

2.2.2. 取出需要的特徵值

此次將成交量納入考慮

1: data = df.filter(['Close', 'Volume'])
2: print(data.head())

2.3. 觀察原始資料/日K圖

1: import matplotlib.pyplot as plt
2: plt.clf()
3: plt.plot(data.Close)
4: plt.show()
5: plt.clf()
6: plt.plot(data.Volume)
7: plt.show()

2.4. 將資料標準化

1: from sklearn.preprocessing import MinMaxScaler
2: scalerX = MinMaxScaler(feature_range=(0, 1))
3: scalerY = MinMaxScaler(feature_range=(0, 1))
4: all_x = data[['Volume', 'Close']]
5: all_y = data['Close']
6: print(all_x.shape)
7: print(all_y.shape)
8: scal_all_x = scalerX.fit_transform(all_x.values)
9: scal_all_y = scalerY.fit_transform(all_y.values.reshape(-1, 1))

2.5. 建立、分割資料

2.5.1. 建立資料集及標籤

 1: import numpy as np
 2: 
 3: # 以前N天的股價來預測未來股價
 4: previousNDays = 10
 5: x_data, y_data = [], []
 6: for i in range(len(scal_all_x) - previousNDays):
 7:   x = scal_all_x[i:i+previousNDays]
 8:   y = scal_all_y[i+previousNDays]
 9:   x_data.append(x)
10:   y_data.append(y)
11: #因為等一下要送進tensorflow
12: x_data, y_data = np.array(x_data), np.array(y_data)
13: 
14: print(x_data.shape)
15: print(y_data.shape)

2.5.2. 分割訓練集與測試集

 1: ratio = 0.8
 2: train_size = round(len(x_data) * ratio)
 3: print(train_size)
 4: x_train, y_train = x_data[:train_size], y_data[:train_size]
 5: x_test, y_test = x_data[train_size:], y_data[train_size:]
 6: 
 7: print(x_train.shape)
 8: print(y_train.shape)
 9: print(x_test.shape)
10: print(y_test.shape)

2.6. 建立、編譯、訓練模型

2.6.1. 建立模型

 1: # CNN模型
 2: import tensorflow as tf
 3: from tensorflow.keras import layers
 4: 
 5: #建構CNN模型
 6: model = tf.keras.Sequential([
 7:     layers.Input(shape=(previousNDays, 2, 1)),
 8:     #第一個卷積層
 9:     layers.Conv2D(filters=512, kernel_size=(3, 1), activation='relu'),
10:     #第二個卷積層
11:     layers.Conv2D(filters=512, kernel_size=(3, 1), activation='relu'),
12:     #攤平
13:     layers.Flatten(),
14:     #全連接層/輸出層(回歸問題不加激活函數)
15:     layers.Dense(1)
16: ])
1: model.summary()

2.6.2. 編譯模型

1: model.compile(loss='mse', optimizer='adam', metrics=['mae'])

2.6.3. 訓練模型

1: model.fit(x_train, y_train,
2:           validation_split=0.2,
3:           batch_size=200, epochs=20)

2.7. 性能測試

2.7.1. loss

1: score = model.evaluate(x_test, y_test)
2: print('loss:', score[0])
3: print('mae:', score[1])

2.7.2. predict

1: predict = model.predict(x_test)
2: predict = scalerY.inverse_transform(predict)
3: predict = np.reshape(predict, (predict.size,))
4: ans = scalerY.inverse_transform(y_test)
5: ans = np.reshape(ans, (ans.size,))
6: print(predict[:3])
7: print(ans[:3])

2.7.3. plot

1: plt.clf()
2: plt.plot(predict, label='Predicted')
3: plt.plot(ans, label='Actual')
4: plt.legend()
5: plt.show()

2.8. 能怎麼胡搞

  • 多讀些原始資料
  • 用更多特徵值來預測
  • 用更多/更少天數來預測
  • 變更模型架構
  • 變更訓練集:測試集比例
  • 增加epoch

3. CNN-3 以AI預測股價-後5日漲跌

為了讓大家都能交出作業,這裡提供一個利用CNN模型的程式參考範例

  • 程式每抓取股票的兩個特徵值
  • 每次預測後5天的股價

請各組自行修改測試

 1: import yfinance as yf
 2: df = yf.Ticker('3260.TWO').history(period='10y')
 3: #挑兩個特徵值
 4: data = df.filter(['Open', 'Close'])
 5: 
 6: from sklearn.preprocessing import MinMaxScaler
 7: #資料標準化
 8: scaler = MinMaxScaler(feature_range=(0, 1))
 9: sc_data = scaler.fit_transform(data.values)
10: 
11: import numpy as np
12: import tensorflow as tf
13: from tensorflow.keras import layers
14: 
15: featureDays = 10 #拿來預測的天數
16: days = 5 #要生成的天數
17: 
18: x_data, y_data = [], []
19: for i in range(len(sc_data) - featureDays - days + 1):
20:   x = sc_data[i:i+featureDays, :]
21:   y = sc_data[i+featureDays: i+featureDays+days, 1]
22:   x_data.append(x)
23:   y_data.append(y)
24: 
25: x_data = np.array(x_data).reshape(-1, featureDays, 2, 1)  # 調整形狀以適應Conv2D
26: y_data = np.array(y_data)
27: 
28: #訓練集、測試集比例
29: ratio = 0.8
30: train_size = round(len(x_data) * ratio)
31: print(train_size)
32: x_train, y_train = x_data[:train_size], y_data[:train_size]
33: x_test, y_test = x_data[train_size:], y_data[train_size:]
34: 
35: #建構CNN模型
36: model = tf.keras.Sequential([
37:     layers.Input(shape=(featureDays, 2, 1)),
38:     layers.Conv2D(filters=512, kernel_size=(3, 1), activation='relu'),
39:     layers.Flatten(),
40:     #輸出層:預測 days 天的收盤價(回歸問題不加激活函數)
41:     layers.Dense(days)
42: ])
43: 
44: model.compile(loss='huber_loss', optimizer='RMSprop', metrics=['mae'])
45: history = model.fit(x_train, y_train, batch_size=100, epochs=20, validation_data=(x_test, y_test))
46: 
47: #評估、預測
48: score = model.evaluate(x_test, y_test)
49: print('loss:', score[0])
50: print('mae:', score[1])
51: 
52: predict = model.predict(x_test)
53: print(predict.shape)
54: print(predict)
55: 
56: #還原scale
57: predict_expanded = np.zeros((predict.shape[0], predict.shape[1], 2))
58: predict_expanded[:, :, 1] = predict
59: 
60: predicted_prices = np.zeros((predict.shape[0], predict.shape[1]))
61: for i in range(predict.shape[1]):
62:     predicted_prices[:, i] = scaler.inverse_transform(predict_expanded[:, i])[:, 1]
63: 
64: print(predicted_prices.shape)
65: print(predicted_prices)

4. LSTM: 以AI預測股價-後5日漲跌

4.1. 參考程式碼

 1: import yfinance as yf
 2: from sklearn.preprocessing import MinMaxScaler
 3: import numpy as np
 4: import tensorflow as tf
 5: from tensorflow.keras import layers
 6: import matplotlib.pyplot as plt
 7: 
 8: df = yf.Ticker('3260.TWO').history(period='10y')
 9: 
10: #挑兩個特徵值
11: data = df.filter(['Open', 'Close'])
12: 
13: # 標準化(LSTM 對數值範圍敏感,不標準化訓練會不穩定)
14: scaler = MinMaxScaler(feature_range=(0, 1))
15: sc_data = scaler.fit_transform(data.values)
16: 
17: featureDays = 10 #拿來預測的天數
18: days = 5 #要生成的天數
19: 
20: x_data, y_data = [], []
21: for i in range(len(sc_data) - featureDays - days + 1):
22:     x = sc_data[i:(i + featureDays), :]
23:     y = sc_data[(i + featureDays):(i + featureDays + days), 1]
24:     x_data.append(x)
25:     y_data.append(y)
26: 
27: x_data, y_data = np.array(x_data), np.array(y_data)
28: 
29: # 訓練集與測試集
30: train_size = int(len(x_data) * 0.8)
31: x_train, x_test = x_data[:train_size], x_data[train_size:]
32: y_train, y_test = y_data[:train_size], y_data[train_size:]
33: 
34: model = tf.keras.Sequential([
35:     layers.Input(shape=(featureDays, 2)),
36:     layers.LSTM(50, return_sequences=True),
37:     layers.LSTM(50, return_sequences=False),
38:     layers.Dense(25),
39:     layers.Dense(days)
40: ])
41: 
42: model.compile(optimizer='adam', loss='mean_squared_error')
43: 
44: history = model.fit(x_train, y_train, batch_size=32, epochs=20, validation_data=(x_test, y_test))
45: 
46: test_loss = model.evaluate(x_test, y_test)
47: print('Test loss:', test_loss)
48: 
49: predictions = model.predict(x_test)
50: 
51: # 還原 scale(只取 Close 欄位)
52: pred_expanded = np.zeros((predictions.shape[0], predictions.shape[1], 2))
53: pred_expanded[:, :, 1] = predictions
54: predicted_prices = np.zeros((predictions.shape[0], predictions.shape[1]))
55: for i in range(predictions.shape[1]):
56:     predicted_prices[:, i] = scaler.inverse_transform(pred_expanded[:, i])[:, 1]
57: 
58: actual_expanded = np.zeros((y_test.shape[0], y_test.shape[1], 2))
59: actual_expanded[:, :, 1] = y_test
60: actual_prices = np.zeros((y_test.shape[0], y_test.shape[1]))
61: for i in range(y_test.shape[1]):
62:     actual_prices[:, i] = scaler.inverse_transform(actual_expanded[:, i])[:, 1]
63: 
64: # 預測與實際價格比較
65: plt.clf()
66: plt.figure(figsize=(10, 6))
67: plt.plot(actual_prices.flatten(), label='Actual Prices')
68: plt.plot(predicted_prices.flatten(), label='Predicted Prices')
69: plt.title('Stock Price Prediction (LSTM)')
70: plt.xlabel('Time')
71: plt.ylabel('Price')
72: plt.legend()
73: plt.savefig('images/stock-lstm-predict.png', dpi=150)
74: plt.show()

stock-lstm-predict.png

4.2. 能怎麼胡搞

  • 多讀些原始資料
  • 用更多特徵值來預測
  • 用更多/更少天數來預測
  • 變更模型架構
  • 變更訓練集:測試集比例
  • 增加epoch

Author: 顏永進

Created: 2026-02-14 Sat 17:32