Table of Contents
超級趨勢指標 ( SuperTrend Indicator ) 是一種技術分析工具,用於識別金融市場中的趨勢走向,幫助投資人判別波段的相對高點及低點,進而做出買賣決策,因此超級趨勢指標特別適合無法判別股價走向,或是常常太早退場、錯過波段漲幅的投資人。不過超級趨勢指標也有在盤整階段會失靈的缺點,所以本文我們將使用平均趨向指標 ( Average Directional Index, ADX 指標 ) 來優化超級趨勢策略。
ADX 指標是由 J. Welles Wilder 於 1978 年研發,代表價格趨勢強度的指標,ADX 指標介於 0~100,數值越大,即代表趨勢越強。因此我們將結合超級趨勢指標與 ADX 指標,有效判斷出趨勢與盤整區間,藉此建構超級趨勢策略,並利用 TQuant Lab 回測其績效,幫助投資人深入了解超級趨勢策略如何賺取波段收益。
上圖為超級趨勢指標於價格曲線上的示意圖,我們可以看到超級趨勢指標能有效地判別出趨勢上漲的區間;但相對的,在盤整區間超級趨勢指標便會失靈,產生交易上的雜訊,因此本文我們將超級趨勢指標搭配可以為我們判斷趨勢強度的 ADX 指標,建構出超級趨勢策略。以下我們將分別介紹超級趨勢指標與 ADX 指標的計算公式,讓我們一窺這兩種指標的奧秘!
雖然在以上的示意圖中,超級趨勢指標看似只由一條曲線組成,不過實際上超級趨勢指標擁有上軌與下軌——當收盤價突破上軌,代表價格突破壓力,即將形成上漲趨勢,此時下軌將成為新的支撐;反之,當收盤價跌破下軌,代表價格跌破支撐,即將形成下跌趨勢,此時上軌則成為新的壓力,因此我們看到的超級趨勢指標即為上漲趨勢中的支撐 ( 下軌 ) 與下跌趨勢中的壓力 ( 上軌 ) 所形成。
為了計算超級趨勢指標的上下軌,我們需要以下三個步驟:
Step1: 計算 ATR
ATR ( Average True Range, 真實波動幅度均值 ) 的計算基於 TR ( True Range, 真實波動幅度 )。TR 代表特定時間內的最大價格變動範圍,通常由以下三個差值中絕對值的最大值來決定:
將 TR 進行一段時間 ( window length ) 的移動平均,即可取得 ATR,一般為 14 日 ATR。
p.s. 更多關於 ATR 的應用可以參考:TQuant Lab 損失規避策略 — 真實波動幅度均值。
Step2: 計算基準上軌與下軌
基準上、下軌公式:
其中,Multiplier 是一個用來調整基準線距離的倍數,一般為 2 或 3。
Step3: 計算最終上軌與下軌
ADX 指標的數值範圍在 0 到 100 之間,其數值的涵義如下:
ADX 指標的計算包含以下四個步驟:
Step1: 計算 ATR
同超級趨勢指標中 ATR 的計算方法。
Step2: 計算 +DM, -DM ( Directional Movement )
Step3: 計算 +DI, -DI ( Directional Indicator )
Step4: 計算 ADX
DX = 100 × ( |+DI – (-DI)| / |+DI + (-DI)| )
將 DX 進行一段時間 ( window length ) 的移動平均,即可取得 ADX。
以下,我們將透過 TQuant Lab 來生成超級趨勢策略所需的交易訊號。
本文使用 Windows 11 並以 Jupyter Lab 作為編輯器。
import os
import numpy as np
import pandas as pd
# tej_key
tej_key = 'your key'
api_base = 'https://api.tej.com.tw'
os.environ['TEJAPI_KEY'] = tej_key
os.environ['TEJAPI_BASE'] = api_base
根據超級趨勢指標的特性,我們希望選取具有成長性且容易形成趨勢的股票,因此我們使用 get_universe
函式取得 2018 年底電子工業的上市股票清單,並透過 TEJ Tool API
篩選市值前 10 名的股票。
from zipline.sources.TEJ_Api_Data import get_universe
pool = get_universe(start = '2018-12-28',
end = '2018-12-28',
mkt_bd_e = 'TSE', # Listed stock in Taiwan
stktp_e = 'Common Stock',
main_ind_c = 'M2300 電子工業' # Electronics Industry
)
import TejToolAPI
mktcap_data = TejToolAPI.get_history_data(start = '2018-12-28',
end = '2018-12-28',
ticker = pool,
columns = ['Market_Cap_Dollars']
)
tickers = mktcap_data.nlargest(10, 'Market_Cap_Dollars')['coid'].tolist()
資料期間從 2019-01-01 至 2024-07-01,並導入上述 10 檔股票的價量資料與加權股價報酬指數 ( IR0001 ) 作為績效比較基準。
start = '2019-01-01'
end = '2024-07-01'
os.environ['mdate'] = start + ' ' + end
os.environ['ticker'] = ' '.join(tickers) + ' ' + 'IR0001'
!zipline ingest -b tquant
有了價量資料,我們就能使用 CustomFactor
函式建構出我們所需的超級趨勢指標上下軌,以及 ADX 指標。此外,為了藉由超級趨勢指標最佳化地判斷出趨勢區間,減少短期的交易雜訊,我們將超級趨勢指標的 ATR 計算區間設為 50 日,並將計算基準上下軌的 Multiplier 設為 4;ADX 指標的計算區間則設為 14 日,以便及時判讀趨勢強弱。
詳細 CustomFactor
因子建構方式可參考 GitHub 原始碼:TQuant Lab 超級趨勢策略。
Pipeline()
提供使用者快速處理多檔標的的量化指標與價量資料的功能,於本次案例我們用以處理:
from zipline.data import bundles
from zipline.pipeline import Pipeline
from zipline.TQresearch.tej_pipeline import run_pipeline
from zipline.pipeline.filters import StaticAssets
bundle = bundles.load('tquant')
benchmark_asset = bundle.asset_finder.lookup_symbol('IR0001',as_of_date = None)
def make_pipeline():
close = TWEquityPricing.close.latest
supertrend = Supertrend()
adx = ADX()
return Pipeline(
columns={
'close': close,
'final_upperband': supertrend.final_upperband,
'final_lowerband': supertrend.final_lowerband,
'ADX': adx.adx
},
screen = ~StaticAssets([benchmark_asset])
)
pipeline_result = run_pipeline(make_pipeline(), start, end)
pipeline_result
initialize()
函式用於定義交易開始前的每日交易環境,與此例中我們設置:
from zipline.finance import slippage, commission
from zipline.utils.calendar_utils import get_calendar
from logbook import Logger, StderrHandler, INFO
from zipline.api import *
# Set up 'log' so we can see trading details when backtesting.
log_handler = StderrHandler(format_string='[{record.time:%Y-%m-%d %H:%M:%S.%f}]: ' +
'{record.level_name}: {record.func_name}: {record.message}',
level=INFO)
log_handler.push_application()
log = Logger('Algorithm')
def initialize(context):
set_slippage(slippage.VolumeShareSlippage())
set_commission(commission.Custom_TW_Commission())
set_benchmark(symbol('IR0001'))
attach_pipeline(make_pipeline(), 'mystrategy')
context.has_ordered = {}
handle_data()
為構建交易策略的重要函式,會在回測開始後每天被呼叫,主要任務為設定交易策略、下單與紀錄交易資訊。
如文章開頭所述,我們希望透過 ADX 指標判別出夠強的趨勢再進行交易,以減少超級趨勢指標無法判讀盤整區間的劣勢,不過在建構買進訊號時,我們並不會避開盤整區間,原因是盤整區間可視為股市休息、能量累積的階段,不失為進場布局的好時機;反之,在建構賣出訊號時,我們才需要搭配 ADX 指標,確保進入下行趨勢再賣出持股,以免錯過先前的波段漲勢。
超級趨勢策略的進出場規則如下:
def handle_data(context, data):
out_dir = pipeline_output('mystrategy')
for i in out_dir.index:
sym = i.symbol
price = out_dir.loc[i, 'close']
ADX = out_dir.loc[i, 'ADX']
final_upperband = out_dir.loc[i, 'final_upperband']
final_lowerband = out_dir.loc[i, 'final_lowerband']
cash_position = context.portfolio.cash
stock_position = context.portfolio.positions[i].amount
buy, sell = False, False
if context.has_ordered.get(f'{i}') is None:
context.has_ordered[f'{i}'] = False
record(
**{
f'price_{sym}': price,
f'buy_{sym}': buy,
f'sell_{sym}': sell
}
)
if stock_position == 0:
if (context.has_ordered[f'{i}'] == False) and (price > final_upperband):
order_target_percent(i, 1/len(tickers))
buy = True
context.has_ordered[f'{i}'] = True
record(**{f'buy_{sym}': buy})
elif stock_position > 0:
if (context.has_ordered[f'{i}'] == True) and (price < final_lowerband) and (ADX > 50):
order_target_percent(i, 0)
sell = True
context.has_ordered[f'{i}'] = False
record(**{f'sell_{sym}': sell})
使用 run_algorithm()
執行上述設定的超級趨勢策略,設置交易期間為 start_dt ( 2019-01-01 ) 到 end_dt ( 2024-07-01 ),所使用資料集為 tquant,初始資金為 10,000,000 元。其中輸出的 results 就是每日績效與交易的明細表。
start_dt = pd.Timestamp(start, tz = 'utc')
end_dt = pd.Timestamp(end, tz = 'UTC')
from zipline import run_algorithm
results = run_algorithm(
start = start_dt,
end = end_dt,
initialize = initialize,
bundle = 'tquant',
analyze = analyze,
capital_base = 1e7,
handle_data = handle_data
)
results
import pyfolio as pf
returns, positions, transactions = pf.utils.extract_rets_pos_txn_from_zipline(results)
benchmark_rets = results.benchmark_return
# Creating a Full Tear Sheet
pf.create_full_tear_sheet(returns=returns,
positions=positions,
transactions=transactions,
benchmark_rets=benchmark_rets
)
透過上表我們可以看到超級趨勢策略的年化報酬率達到 25.88%,年化波動度則約為相對較低的 13%。若進一步觀察績效指標,夏普比率為 1.84,索提諾比率則為 2.82,顯示超級趨勢策略能有效迴避下行風險,並在承受較低的風險下賺取超額報酬。觀察報酬曲線圖,雖然超級趨勢策略並沒有明顯超過大盤的績效,不過我們可以發現 2021 年初達到波段高點後的報酬趨於水平到 2022 年中,成功避免了 2022 熊市的大回檔,並且在 2023 年後也有不錯的回升趨勢,可見超級趨勢策略鎖住波段獲利、提前規避下行趨勢風險的特性。
在回檔比率圖中,可見最大的兩次回檔發生在 2020 年初的疫情恐慌以及 2022 年的熊市,不過相較於大盤兩次皆有約 30% 的回檔,超級趨勢策略的 16% 及 14 % 回檔已是相對較小的了。
我們藉由 Pipeline
輸出的資料表,以及 run_algorithm()
產出的 results 交易明細表額外建立了一個函式 graph()
幫助我們了解個別股票於超級趨勢策略的表現。
graph()
建構方式可參考 GitHub 原始碼:TQuant Lab 超級趨勢策略。
上圖中紅點為進場點,綠點為出場點。可以看到超級趨勢策略在 2019 到 2020 年的上行趨勢中成功獲利,不過在 2021 年的盤整階段以及 2022 年的下行趨勢中卻有買在相對高點的失靈狀況,所幸我們透過 ADX 指標優化的賣點限縮了部分的跌幅,最後於 2023 年的買點也成功抱住波段獲利到 2024 年 7 月。
本次策略由超級趨勢指標進行發想,而有鑑於超級趨勢指標於盤整階段的失靈現象,我們也使用 ADX 指標來優化策略,減少短期的交易雜訊。透過 Pyfolio 產出的績效分析圖表,我們觀察到超級趨勢策略有效規避下行趨勢的特性;另外我們也查看台積電在本策略的表現,結果發現策略確實有把握波段漲幅的效果。
需要提醒投資朋友的是,本文在策略建構上涉及股票池的選定以及指標計算的參數設定,不同股票池與參數皆可能對績效表現有所影響,對本策略有興趣的投資朋友可以嘗試不同的設定,建構最有效益的投資策略。
溫馨提醒,本次策略與標的僅供參考,不代表任何商品或投資上的建議。之後也會介紹使用 TEJ 資料庫來建構各式指標,並回測指標績效,所以歡迎對各種交易回測有興趣的讀者,選購 TQuant Lab 的相關方案,用高品質的資料庫,建構出適合自己的交易策略。
電子報訂閱