請更新您的瀏覽器

您使用的瀏覽器版本較舊,已不再受支援。建議您更新瀏覽器版本,以獲得最佳使用體驗。

理財

麥克.墨菲高科技股投資風險評估法則

TEJ 台灣經濟新報

更新於 05月21日13:23 • 發布於 05月20日06:00
Photo by Opt Lasers on Unsplash

本文重點摘要

  • 文章難度:★★☆☆☆
  • 高科技股選股策略。
  • 透過 TQuant Lab 回測麥克.墨菲策略,觀察此策略因子績效。

前言

隨著高科技產業迅速發展,其股票也常成為市場焦點。然而,高科技股雖具備成長潛力,卻同時伴隨著極高的波動性與投資風險。投資人在追求高報酬的同時,若未妥善評估風險,容易面臨重大虧損。因此,如何有效衡量與控管高科技股的下跌風險,成為投資決策中不可忽視的一環。

如何計算因子 & 透過因子進行篩選

選股

在TQuant Lab中,我們使用電子工業當我們股票池再進行篩選,可以針對使用者需求調整自己需要的科技業類別。

計算因子,下跌風險值&偏離值

以下資料使用欄位皆取自於Tquant Lab資料集

A :每股營業額 : 近12月累計營收_千元/流通在外股數

B : 每股帳面價值 : 股東權益總計_Q / 流通在外股數

C : 過去3年平均稅前盈餘成長率 : 稅前淨利成長率,取序號1的Q欄位,然後用pivot轉為矩陣後直接rolling(12).mean

D : 過去4季每股盈餘 : 每股淨值_TTM

E : 下跌風險值 (DRV) = ( A + 1.5 * B + C * D * 1/3) / 3

  • 如果 C 或是 D 為負數,則用0代替

偏離值 : ( 股價 - 下跌風險值 ) / 股價

偏離值與風險的關係 : 正數代表具有大跌風險,正數越大風險越大,小於0表示可以投資,負值越大越安全。

交易邏輯

  • 交易頻率:每月 15 日進行交易,若當天非交易日,則移至下一個開市日。
  • 取消所有未成交訂單,避免影響新交易。
  • 清空本期未被選擇的持股,續抱本期集上一期皆被選擇的股票,節省手續費。
  • 選出「偏離值最小的前 40 檔股票」 作為買進標的。

資金分配

  • 每次進場資金 = 100% 總資金
  • 每檔股票分配資金為等資金分配

載入套件

import pandas as pd import numpy as np import tejapi import os import matplotlib.pyplot as plt import datetime plt.rcParams['font.family'] = 'Arial' os.environ['TEJAPI_BASE'] = "YOUR BASE" os.environ['TEJAPI_KEY'] = "YOUR KEY" from zipline.sources.TEJ_Api_Data import get_universe import TejToolAPI from zipline.data.run_ingest import simple_ingest from zipline.api import set_slippage, set_commission, set_benchmark, symbol, record, order_target_percent from zipline.finance import commission, slippage from zipline import run_algorithm import pandas as pd from zipline.pipeline.data import Column, DataSet from zipline.pipeline.loaders.frame import DataFrameLoader from zipline.pipeline import Pipeline from zipline.pipeline.engine import SimplePipelineEngine from zipline.pipeline.domain import TW_EQUITIES from zipline.pipeline.factors import CustomFactor

取得股票池

在選擇回測的時間點時,選擇電子工業。在此步驟中,可以檢查股票池是否包含具有不喜歡特性的股票,並根據需求進行適當調整。

pool = get_universe(start = '2018-01-01', end = '2024-12-31', mkt_bd_e = ['TSE', 'OTC', 'TIB'], # 已上市之股票 stktp_e = 'Common Stock', # 普通股 main_ind_c = 'M2300 電子工業,OTC23 OTC 電子類') # general industry 可篩掉金融產業

取得資料&整理資料

透過 tejapi 獲取稅前淨利成長率,進而去計算上述數據C (過去3年平均稅前盈餘成長率)

data_r404 = tejapi.get('TWN/AINVFQ1', mdate = {'gte':'2015-01-01', 'lte': '2024-12-31'}, coid = pool ,opts = {'columns':['coid','mdate','r404' , 'no' , 'key3']}, chinese_column_name = True,paginate = True) data_r404 = data_r404[(data_r404['序號'] == '001') & (data_r404['期間別'] == 'Q')] pivot_data_r404 = data_r404.pivot(index='年/月', columns='公司', values='稅前淨利成長率') # 確保 index 是 datetime 格式 pivot_data_r404.index = pd.to_datetime(pivot_data_r404.index) pivot_data_r404 = pivot_data_r404.sort_index() # 滾動12筆平均 rolling_mean = pivot_data_r404.rolling(window=12, min_periods=12).mean() # 只保留每年 12 月 1 日 annual_rolling_mean = rolling_mean[rolling_mean.index.strftime('%m-%d') == '12-01'] # 刪除 2015 和 2016 年的 row(只保留 2017-12-01 起的) annual_rolling_mean = annual_rolling_mean[annual_rolling_mean.index.year >= 2017] annual_rolling_mean = annual_rolling_mean.dropna(axis=1)

接著透過 TejToolAPI 將其餘資料也一併抓齊,接著就能將資料作合併

columns = ['近12月累計營收_千元', 'Outstanding_Shares_1000_Shares', '股東權益總計', '每股淨值' , '收盤價' , '調整係數'] start_dt = pd.Timestamp('2015-01-01', tz = 'UTC') end_dt = pd.Timestamp('2024-12-31', tz = "UTC") data = TejToolAPI.get_history_data(start = start_dt, end = end_dt, ticker = pool, fin_type = ['A' , 'Q' , 'TTM'], columns = columns, transfer_to_chinese = True) # 將 pivot table 攤平為 long format rolling_long = annual_rolling_mean.copy() rolling_long = rolling_long.stack().reset_index() # 重命名欄位 rolling_long.columns = ["年/月", "股票代碼", "過去3年平均稅前盈餘成長率"] # 建立 year 欄 rolling_long["year"] = rolling_long["年/月"].dt.year rolling_long = rolling_long.drop(columns=["年/月"]) # 如果不需要年/月就刪掉 # 先從 data 中取出年份 data["year"] = pd.to_datetime(data["日期"]).dt.year # 合併:依據 股票代碼 和 年份 merged_data = pd.merge(data, rolling_long, how="left", on=["股票代碼", "year"])

有了merged_data就能計算下跌風險值以及偏離值

data = merged_data data['日期'] = pd.to_datetime(data['日期']) # 確保日期格式正確 data['月份'] = data['日期'].dt.to_period('M') # 轉換成月份 (YYYY-MM) data['每股營業額'] = data['近12月累計營收_千元'] / data['流通在外股數_千股']#A data['每股帳面價值'] = data['股東權益總計_Q'] / data['流通在外股數_千股']#B data['年度'] = data['日期'].dt.year # 取平均,確保三者都是平等的權重 #C已經完成 data['過去4季每股盈餘'] = data['每股淨值_TTM']#D # 計算 C × D data['C_D'] = data['過去3年平均稅前盈餘成長率'] * data['過去4季每股盈餘'] # 若 C × D 為負數,則改為 0 data.loc[data['C_D'] < 0, 'C_D'] = 0 # 計算 E(下跌風險值) data['下跌風險值'] = (data['每股營業額'] + data['每股帳面價值'] * 1.5 + data['C_D'] * (1/3)) / 3 # 刪除不必要的中間變數 data.drop(columns=['C_D'], inplace=True) data['偏離值'] = (data['收盤價'] * data['調整係數'] - data['下跌風險值']) / (data['收盤價'] * data['調整係數'])

經過一系列的計算後我們得到偏離值,接著就能把資料匯入,詳細程式碼可以參考相關連結

建立 CustomDataset 函式 & 建立 Pipeline 函式

這段程式碼的架構是透過 Zipline 的 Pipeline 系統,自訂一個名為 Bias_Value 的資料欄位(CustomDataset),將事先處理好的偏離值資料載入後,建立一個選股策略(Pipeline),挑選偏離值為負且最小的前 10 檔股票作為多頭標的,最後透過 SimplePipelineEngine 執行整體流程,回傳指定期間內的選股結果。

from zipline.pipeline.data.dataset import Column, DataSet from zipline.pipeline.loaders.frame import DataFrameLoader transform_data = data5.unstack('SID') transform_data # 確保索引為 UTC fixed_transform_data = transform_data.copy() # 如果索引已經有時區,則用 tz_convert 轉換 if fixed_transform_data.index.tz is not None: fixed_transform_data.index = fixed_transform_data.index.tz_convert('UTC') else: fixed_transform_data.index = fixed_transform_data.index.tz_localize('UTC') class CustomDataset(DataSet): Bias_Value = Column(dtype=float) domain = TW_EQUITIES # 建立 DataFrameLoader Custom_loader = { CustomDataset.Bias_Value: DataFrameLoader(CustomDataset.Bias_Value, fixed_transform_data['偏離值']) } from zipline.pipeline.data import EquityPricing def choose_loader(column): if column.name in EquityPricing.column_names: return pricing_loader elif column.name in CustomDataset.column_names: return Custom_loader[column] else: raise Exception('Column not available') engine = SimplePipelineEngine(get_loader = choose_loader, asset_finder = bundle.asset_finder, default_domain = TW_EQUITIES) from zipline.pipeline import Pipeline def compute_signals_debug(): bias = CustomDataset.Bias_Value.latest return Pipeline(columns={'偏離值(G)': bias}) pipeline_debug = engine.run_pipeline(compute_signals_debug(), start_dt, end_dt) # 篩選出 偏離值\(G\) 不是 NaN 的行 valid_pipeline_debug = pipeline_debug[pipeline_debug['偏離值(G)'].notna()] def compute_signals(): # **篩選出負的偏離值** signals = CustomDataset.Bias_Value.latest negative_bias_filter = signals < 0 # 只選偏離值為負的 # **選擇最負的前 40 檔** longs = signals.bottom(40, mask=negative_bias_filter) # 取最小的 40 檔(偏離值最負) return Pipeline(columns={ 'signals': signals, # 直接存偏離值 'longs': longs # 選最負的前 40 檔作為買進標的 }) pipeline_result = engine.run_pipeline(compute_signals(), start_dt, end_dt)

麥克莫菲

建立 Initialize 函式

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

def initialize(context): """ Called once at the start of the algorithm. """ context.universe = assets context.tradeday = tradeday context.set_benchmark(symbol('IR0001')) context.longs = [] context.shorts = [] # 交易成本 #set_commission(commission.PerDollar(cost=commission_cost)) set_commission(commission.Custom_TW_Commission()) set_slippage(slippage.TW_Slippage( spread = 1.0,volume_limit=0.8)) # schedule_function schedule_function(func=rebalance, date_rule=date_rules.every_day(), time_rule=time_rules.market_open) schedule_function(func=record_vars, date_rule=date_rules.every_day(), time_rule=time_rules.market_close) pipeline = compute_signals() attach_pipeline(pipeline, 'signals')

建立 rebalance 函式

這段程式碼是 Zipline 策略中的再平衡函數,功能如下:

在指定交易日內執行,取消所有未成交訂單,根據 context.trades 中訊號為 1 的股票作為做多標的。這些股票會等資金分配。同時,賣出不再符合條件的持股,對於新選及續抱的股票則下單調整到新目標權重,完成投資組合的動態調整。

from collections import defaultdict def rebalance(context, data): """ 1. 只在 context.tradeday 裡的日期執行 2. 取消所有未成交訂單 3. 從 context.trades 取出 long_candidates(signal == 1) 4. 用偏離值絕對值計算每檔股票的新目標權重 5. 賣掉「失選」的(signal != 1 & 目前持有) 6. 對新進 + 重疊的,每檔都 order_target_percent 到它的新目標權重 """ today = get_datetime().strftime('%Y-%m-%d') if today not in context.tradeday: return # 2. 取消所有未成交訂單 for asset, orders in get_open_orders().items(): for o in orders: cancel_order(o) # 3. 這期要做多的股票 long_candidates = [ stock for stock, signal in context.trades.items() if signal == 1 ] # 沒有候選股,就全部清空 if not long_candidates: for pos in context.portfolio.positions.values(): if pos.amount > 0: order_target_percent(pos.asset, 0) return # 4. 計算偏離值絕對值權重 abs_signal = context.output.loc[long_candidates, 'signals'].abs() total_bias = abs_signal.sum() target_weights = { stock: (1 / len(long_candidates)) for stock in long_candidates } # 5. 現有持股 held = [ pos.asset for pos in context.portfolio.positions.values() if pos.amount > 0 ] # 只賣掉「失選」的 dropouts = set(held) - set(long_candidates) for stock in dropouts: order_target_percent(stock, 0) # 6. 新進 + 重疊 的都調整到新目標權重 # order_target_percent 會自動「加減部位到達目標比例」 for stock, w in target_weights.items(): order_target_percent(stock, w)

回測策略

使用 run_algorithm() 來執行上述設定的動能策略,資料集使用 tquant,初始資金設定為 1,000,000 元。執行過程中,輸出的 results 包含每日績效和交易明細。

在進行繪圖時,為了避免字體錯誤,先進行字體設定:

from zipline import run_algorithm from zipline.utils.calendar_utils import get_calendar capital_base = 1e6 calendar_name = 'TEJ' start_dt = pd.Timestamp(start, tz = 'UTC') end_dt = pd.Timestamp(end, tz = "UTC") # Running a Backtest results = run_algorithm(start=start_dt, end=end_dt, initialize=initialize, before_trading_start=before_trading_start, capital_base=capital_base, data_frequency='daily', analyze=analyze, bundle=bundle_name, trading_calendar=get_calendar(calendar_name), custom_loader=Custom_loader)

利用 Pyfolio 進行績效評估(無止盈、無止損

import pyfolio as pf from pyfolio.utils import extract_rets_pos_txn_from_zipline from pyfolio.plotting import (plot_perf_stats, show_perf_stats, plot_rolling_beta, plot_rolling_returns, plot_rolling_sharpe, plot_drawdown_periods, plot_drawdown_underwater) from pyfolio.tears import * from pyfolio.timeseries import (perf_stats, extract_interesting_date_ranges, sharpe_ratio, sortino_ratio) import empyrical returns, positions, transactions = pf.utils.extract_rets_pos_txn_from_zipline(results) pf.tears.create_full_tear_sheet(returns, positions=positions, benchmark_rets=results['benchmark_return'], transactions=transactions)

績效圖

績效指標 / 策略 大盤(Benchmark) 麥克莫非投資策略 年化報酬率 16.275% 15.767% 累積報酬率 177.386% 169.287% 年化波動度 17.279% 19.856% 夏普值 0.96 0.84 卡瑪比率 0.57 0.45 期間最大回撤 -28.553% -35.045% 從整體績效指標來看,雖然麥克莫非投資策略的年化報酬率(15.767%)與累積報酬率(169.287%)僅略低於大盤(分別為16.275%與177.386%),且在風險控制方面表現相對較弱。該策略的年化波動度為19.856%,高於大盤的17.279%,夏普值為0.84,亦低於大盤的0.96,顯示在承擔更高風險的情況下,其單位風險報酬不如大盤。此外,該策略的期間最大回撤達 -35.045%,亦大於大盤的 -28.553%,代表策略在市場波動期間面臨較大資本損失風險。整體而言,麥克莫非策略雖具備一定報酬能力,但在風險調整後的績效與資金防禦能力上,仍略遜於大盤表現。

從年化報酬圖顯示策略在多數年度均有不錯表現,尤其在 2023 年年報酬超過 40%,顯示策略在特定市場環境下具備強勁的獲利能力;雖然 2018 與 2022 年出現負報酬,這部分可以視情況設定止盈止損。

結論

本策略聚焦於高科技產業,從中篩選出偏離值最小的前 40 檔個股,偏離值越低代表該股票與基準價格的落差越小,反映其價格相對穩定、下跌風險較低。科技業雖具有高成長潛力,但波動性大、風險高,因此透過此策略能有效辨識出在高風險環境中相對穩健的投資標的。從回測結果來看,策略報酬接近市場,還展現出良好的風險控制能力,但是相對也因為風險的降低,篩選掉了一些飆股的可能性,導致有時大盤在漲反而此策略的選股沒有明顯向上趨勢。

歡迎投資朋友參考,之後也會持續介紹TEJ資料庫來建構各式指標,並回測指標績效,所以歡迎對各種交易回測有興趣的讀者,選購TQuant Lab的相關方案,用高品質的資料庫,建構出適合自己的交易策略。

溫馨提醒,本次分析僅供參考,不代表任何商品或投資上建議。

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

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

點我註冊會員,開始試用

Github 原始碼

點此前往 Github

延伸閱讀

相關連結

查看原始文章

更多理財相關文章

01

ATM新制今上路!8大銀行「宣布新規」 有錢也領不出來

TVBS
02

特斯拉AI晶片選三星非台積?分析師曝關鍵!

NOWnews 今日新聞
03

股市觀察》兩周內決戰!台股要崩了?分析師揭「假利空真洗盤」真相

新頭殼
04

海角36億元拿不回外傳要賣街口電支股票 泰山反應曝光

鏡週刊
05

AI股太貴、非龍頭股不敢買?億級投資人:現在這種產品是好選擇

商周.com
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
查看更多

留言 0

沒有留言。

最新消息

鉅亨買幣速報 - 以太幣(ETH)24小時成交量超過22.6億美元,LTO Network(LTO)24小時漲幅達54.8%

anue鉅亨網

輝達H20晶片重啟對中國出口 傳向台積電追加代工訂單

商傳媒

矽谷最神秘公司 Palantir 現身台灣,自揭市場差異化的關鍵優勢

TechOrange 科技報橘

〈國巨法說〉Q2業外影響每股純益1.03元 上半年仍賺逾2股本

anue鉅亨網

盤後速報 - 大台北(9908)次交易(30)日除息1.2元,參考價30.05元

anue鉅亨網

盤後速報 - 大汽電(8931)次交易(30)日除息2元,參考價50.8元

anue鉅亨網

盤後速報 - 上洋(6728)次交易(30)日除息8元,參考價153.0元

anue鉅亨網

盤後速報 - 伊雲谷(6689)次交易(30)日除息2元,參考價79.1元

anue鉅亨網

盤後速報 - 動力-KY(6591)次交易(30)日除息2.35元,參考價64.75元

anue鉅亨網

盤後速報 - 瑞祺電通(6416)次交易(30)日除息2.9元,參考價84.4元

anue鉅亨網

盤後速報 - 大城地產(6171)次交易(30)日除息2.5元,參考價33.1元

anue鉅亨網

盤後速報 - 久威(6114)次交易(30)日除息0.5元,參考價35.75元

anue鉅亨網

盤後速報 - 中租-KY甲特(5871A)次交易(30)日除息3.8元,參考價98.2元

anue鉅亨網

盤後速報 - 中租-KY(5871)次交易(30)日除權息6.3元,參考價118.04元

anue鉅亨網

盤後速報 - 遠雄港(5607)次交易(30)日除權息1.3元,參考價43.24元

anue鉅亨網

盤後速報 - 宣德(5457)次交易(30)日除息1.3元,參考價42.0元

anue鉅亨網

盤後速報 - 國眾(5410)次交易(30)日除息1.5元,參考價27.25元

anue鉅亨網

盤後速報 - 強信-KY(4560)次交易(30)日除息1.2元,參考價34.0元

anue鉅亨網

盤後速報 - 冠星-KY(4439)次交易(30)日除息2元,參考價90.0元

anue鉅亨網

盤後速報 - 東隆興(4401)次交易(30)日除息0.7元,參考價15.45元

anue鉅亨網

盤後速報 - 世坤(4305)次交易(30)日除息3.2元,參考價45.55元

anue鉅亨網

盤後速報 - 中華食(4205)次交易(30)日除權息3元,參考價86.91元

anue鉅亨網

AI需求助攻,國巨Q2本業獲利站11季高點,Q3營收、雙率向上

財訊快報

盤後速報 - 先益(3531)次交易(30)日除息0.8元,參考價22.75元

anue鉅亨網

盤後速報 - 晶睿(3454)次交易(30)日除息2.3元,參考價104.2元

anue鉅亨網

盤後速報 - 弘憶股(3312)次交易(30)日除息1.4元,參考價45.7元

anue鉅亨網

盤後速報 - 綠意(2596)次交易(30)日除息1元,參考價30.05元

anue鉅亨網

盤後速報 - 皇普(2528)次交易(30)日除權0.5元,參考價27.43元

anue鉅亨網

盤後速報 - 怡利電(2497)次交易(30)日除息2元,參考價39.0元

anue鉅亨網

盤後速報 - 技嘉(2376)次交易(30)日除息10元,參考價272.5元

anue鉅亨網

盤後速報 - 矽統(2363)次交易(30)日除息0.5元,參考價55.1元

anue鉅亨網

盤後速報 - 智邦(2345)次交易(30)日除息11元,參考價841.0元

anue鉅亨網

盤後速報 - 宇隆(2233)次交易(30)日除息7元,參考價172.0元

anue鉅亨網

盤後速報 - 力肯(1570)次交易(30)日除息1.3元,參考價22.6元

anue鉅亨網

盤後速報 - 元山(6275)下週(8月5日)除息2.26元,預估參考價41.39元

anue鉅亨網

盤後速報 - 達麗(6177)下週(8月5日)除權息4元,預估參考價40.24元

anue鉅亨網

盤後速報 - 宜特(3289)下週(8月5日)除息1元,預估參考價121.0元

anue鉅亨網

盤後速報 - 力特(3051)下週(8月5日)除息1.52元,預估參考價23.78元

anue鉅亨網

盤後速報 - 臺企銀(2834)下週(8月5日)除權息0.8元,預估參考價15.14元

anue鉅亨網

盤後速報 - 根基(2546)下週(8月5日)除權息3.8元,預估參考價77.17元

anue鉅亨網