理財

利用 TQuant Lab 驗證深度學習 LSTM 股價預測成效(二)

TEJ 台灣經濟新報
更新於 08月06日14:23 • 發布於 08月07日05:00
Photo by Alina Grubnyak on Unsplash

本文重點概要

  • 文章難度:★★★★★
  • 結合基本面、籌碼面及技術面資料進行 LSTM 股價預測,並執行回測驗證績效
  • 閱讀建議:本文使用 RNN 架構進行時間序列預測,需要對時間序列或是深度學習有基礎瞭解,可以參考【資料科學】LSTM,以便對於 LSTM 模型建置有更深的理解。

前言

廣告(請繼續閱讀本文)

在第一篇文章—利用 TQuant Lab 驗證深度學習 LSTM 股價預測成效(一),我們利用預測數據與實際數據的比對,對於訓練好的兩個模型(2618、8615 兩檔股票)有初步的成效評估,效果是不錯的,詳細分析可以點擊以上連結了解更多,篇幅原因就不贅述。
因此在第二篇文章,我們希望不要只是紙上談兵,將模型套用在 out sample 的資料,並根據預測結果決定進場點,實測看看效果是不是也如預期的好。

編輯環境及模組需求

本文使用 Mac OS 及 VS Code 作為編輯器

廣告(請繼續閱讀本文)

LSTM 模型套用樣本外資料

我們的兩個模型的樣本內資料都是自 2012-07-012021-07-01 ,因此此次的回測起訖日會從 2021-01-012024-06-30,以避開訓練期間。

載入外部套件

import os import time import tejapi import talib as ta from talib import abstract import numpy as np import pandas as pd …

載入內部套件

ML_stock() 為我們編寫用以做事前資料處理的 class,執行載入 API_KEY、價量資料、基本面資料及技術指標等主要功能。最後設定回測的樣本起訖日。
另外我們設置 model 變數,將訓練好的模型載入以待使用。
*註:貼心提醒,在使用前請先至 config.ini 輸入自己的 API_KEY,這樣才能順利使用喔!

ml_stock = ML_stock() ml_stock.ini() model = load_model(f'lstm_{sample[1]}.keras') start = '2021-01-01' end = '2024-06-30' os.environ['mdate'] = start + ' ' + end os.environ['ticker'] = ' '.join([sample[1]]) + ' ' + 'IR0001' !zipline ingest -b tquant

接下來我們只留下必要的特徵,完成資料前處理。

df = ml_stock.get_fundamental(start, end, [sample[1]], column) df = ml_stock.calculate_all_technical_indicators(df) preporc_data = ml_stock.preprocessing(df) data = preporc_data.drop(columns=['mdate', 'coid'])

資料前處理

建立時間序列資料

與訓練時一樣,我們將資料轉換成時間序列資料,具體流程可以閱讀上一篇說明。轉換好之後,便使用 predict 函數套用模型進行預測。

prediction = model.predict(X)

2618 預測結果
8215 預測結果

可以看到我們以 2022 過去十年的資料所訓練的 LSTM 模型套用在 2022 年之後的股價的預測表現也與實際上大致相符,除了在預測驗證資料就會出現的漲跌幅跟不上以外,基本上沒有問題。

將預測結果導入 Pipeline

CustomDataset 可以將資料庫中的內容導入 Pipeline 中,方便後續回測使用。於本範例我們用以將上述 Pred 欄位紀錄的預測數值資訊導入 Pipeline。擷取部分程式碼如下:

from zipline.pipeline.data.dataset import Column, DataSet from zipline.pipeline.domain import TW_EQUITIES class CustomDataset(DataSet): pred = Column(float) domain = TW_EQUITIES

Pipeline 導入結果—2618

建立 Pipeline 函式

由於 LSTM 模型只是做了對隔日收盤價的預測,具體該如何進場、進場時機及條件為何,還需要我們做更精細的設定,為此需要設計客製化因子。

建立 Custom Factor

CustomFactor 可以讓使用者自行設計所需的客製化因子,於本次案例我們用以處理:

  • 相對於上個交易日的每日報酬率(Return)
  • 平均真實波幅(AverageTrueRange)def make_pipeline(): pred = CustomDataset.pred.latest price = TWEquityPricing.close.latest returns = Return(inputs=[TWEquityPricing.close]) returns_pred = Return() ATR = AverageTrueRange(inputs = [TWEquityPricing.high, TWEquityPricing.low, TWEquityPricing.close]) return Pipeline( columns = { 'price': price, 'pred': pred, 'return': returns, 'return_pred': returns_pred, 'atr': ATR.ATR, }, screen = ~StaticAssets([benchmark_asset]) ) pipeline_result = engine.run_pipeline(make_pipeline(), start_dt, end_dt) pipeline_result
Pipeline 輸出結果—2618

建立 initialize 函式

initialize() 函式用於定義交易開始前的每日交易環境,與此例中我們設置:

  • 滑價成本
  • 台股市場手續費模型
  • 加權報酬指數 ( IR0001 ) 作為大盤指數
  • 將 Pipeline 設計的策略因子導入交易流程中
  • 設定 context.stop_loss 變數,將回測中的止損點紀錄
  • 設定 context.last_price 變數,記錄最後買賣價格,跟蹤止損

建立 handle_data 函式

handle_data() 為構建策略的重要函式,會在回測開始後每天被呼叫,主要任務為設定交易策略、下單與紀錄交易資訊。

關於本策略的交易詳細規則請至:backtest_2618.ipynb / backtest_8215.ipynb

if return_pred < 0 and cash_position >= 0 and returns < 0: order_percent(i , 0.48) buy = True record( **{ f'buy_{sym}':buy } ) context.stop_loss = price - atr * 1.25

本次範例利用 return_pred 紀錄預測股價、returns 為真實股價走向,若兩者走勢為正向,則進行入場。出場則是以停利和跟蹤止損並行。本策略只回測長部位的單向入場,有興趣者可以再嘗試多空並行的策略。

建立 analyze 函式

analyze() 協助我們繪製自訂圖表,本次我們用到 analyze()來觀察預測股價和實際股價的差異、策略進出場情形和資金運用情況。

關於本策略的交易詳細規則請至:backtest_2618.ipynb / backtest_8215.ipynb

執行 LSTM 股價預測策略

from zipline import run_algorithm start = '2021-01-01' end = '2024-06-28' start_dt = pd.Timestamp(start, tz = 'UTC') end_dt = pd.Timestamp(end, tz = 'UTC') results = run_algorithm( start = start_dt, end = end_dt, initialize = initialize, bundle = 'tquant', analyze = analyze, capital_base = 1e6, handle_data = handle_data, custom_loader=custom_loader ) results

使用 run_algorithm() 執行上述設定的策略,設置交易期間為 start_dt(2021-01-01) 到 end_dt(2024-06-28),導入 custom_loader,使用資料集 tquant,初始資金為一百萬元。其中輸出的 results 為每日績效與交易的明細表。

analyze 分析圖表–2618
analyze 分析圖表–8215

上面兩張圖顯示策略獲得良好的績效,其中 2618 的回測全程超越大盤,而 8215 的績效則是在領先大盤後被大盤稍稍追過。
*注:圖中第二小圖為預測股價和實際股價的差異,藍線為預測股價,紅線則為實際股價,紅色三角標記為買進,綠色三角標記為賣出。

利用 Pyfolio 進行績效評估

import pyfolio as pf returns, positions, transactions = pf.utils.extract_rets_pos_txn_from_zipline(results) benchmark_rets = results['benchmark_return'] pf.tears.create_full_tear_sheet(returns=returns, positions=positions, transactions=transactions, benchmark_rets=benchmark_rets )

回測表現與大盤比較圖—2618
回測表現與大盤比較圖—8215

結論

在以上兩檔股票的回測中,我們可以看到以 LSTM 股價預測策略進行回測的績效會有不錯的成果,兩檔回測的夏普率都超過 0.5,Alpha 值也都在一個不錯的水準。比較特別的是 2618 這檔回測,40 個月來年化報酬率來到 29.6%,累積報酬率更來到接近 138%,績效相當的好。
更詳細的來看看策略本身的特性,由於策略的基礎是以隔日收盤價為目標進行買入,因此我們可以發現到報酬率曲線相當近似於實際股價的曲線,換句話說就是漲的時候策略會漲,跌的時候策略也會跌,因此止損與止盈的控制就非常重要,需要特別注意。
另外我們在撰寫策略時,有發現到「return_pred 紀錄預測股價、returns 為真實股價走向,若兩者走勢為正向,則進行入場。」這件事不全然可以套用在所有模型上,可能對於某些檔股票而言,走勢同為負向時,抑或是正 / 負得更多時再進行入場才會獲得更高的收益,否則績效的變化是很大的。
之所以會導致此原因,我們推測是由於時間序列模型在預測上的延遲性,這個延遲性會導致我們在買多時可能會有不小心買在高點(因為高點在前一天就出現了,模型第二天才反映出來)導致成本過高,或是賣在低點(低點在前一天就出現,模型同樣是第二天才反映,導致績效變差),因此這項策略會更需要我們多去調整策略參數,找到最優解。

歡迎投資朋友參考,之後也會持續介紹使用 TEJ 資料庫來建構各式指標,並回測指標績效,所以歡迎對各種交易回測有興趣的讀者,選購 TQuant Lab 的相關方案,用高品質的資料庫,建構出適合自己的交易策略。
溫馨提醒,本次分析僅供參考,不代表任何商品或投資上的建議。

【TQuant Lab回測系統】解決你的量化金融痛點

全方位提供交易回測所需工具

點我註冊會員,開始試用

原始碼

點此前往 GitHub

延伸閱讀

利用 TQuant Lab 驗證深度學習 LSTM 股價預測成效(一)

LSTM 交易訊號判斷

LSTM

查看原始文章