請更新您的瀏覽器

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

理財

別人恐懼我進場:安東尼·賣利亞 的反向市場致勝術

TEJ 台灣經濟新報

更新於 2025年09月12日10:13 • 發布於 2025年09月12日06:00
Photo by Markus Spiske on Unsplash

前言

在金融市場中,「反向思考(Contrary Thinking)」是一種歷久不衰的智慧策略。它源於一個簡單但深刻的觀察:當大多數人極度樂觀時,市場往往已經過熱;當群眾陷入恐懼時,反而可能是低接良機。然而,反向操作長期以來多停留在諺語層次,缺乏具體、可量化的行動標準,導致實務上難以執行。

安東尼‧賈利亞(Anthony M. Gallea)與威廉‧巴特隆(William Patalon III)於《Contrarian Investing》一書中,首度將反向操作具體化為可執行的量化選股策略。賈利亞是所羅門美邦公司(Salomon Smith Barney)資深投資組合管理董事,帶領逾14人團隊、管理超過六億美元資產;巴特隆則為資深商業記者,曾四度獲得紐約州美聯社頒發之商業報導獎。兩人以豐富的實務與觀察經驗,構建出一套兼具理論深度與實用性的選股方法。

策略邏輯

本策略採用多層次篩選邏輯,結合技術面、籌碼面與基本面條件,從中挑選出具備中期成長潛力的股票。首先,在技術面上,篩選最近一年內收盤價低於一年內最高價的 50% 的股票,作為初步具備反轉動能潛力的候選名單。

接著,在籌碼面條件中,若董事或經理人在最近六個月內持股比例增加至少 5%,或持股比例已達 10% 以上,則視為內部人具備明確信心的標的,亦納入考量;此外,投信若出現明顯加碼行為,也具備相同效力。若籌碼面條件滿足,則進一步檢視該公司是否具備基本面優勢,要求其符合以下四項條件中的至少兩項:

  • 市盈率(PE)低於市場同行平均市盈率的 85%

  • 價格 / 自由現金流量比 < 1 倍

  • 價格 / 每股淨值比 < 0.8 倍

  • 價格 / 每股營收比 < 1 倍

選出標的股票之後, 以等權重的方式去分配總體資金,以達到分散風險的效果,不爾會將資金過度集中於單一股票中。

抓取 TEJ 資料欄位

資料代碼 close_d adjfac fld005 qfii_pct fd_pct ri 科目 收盤價 調整係數 董監持股% 外資持股率 投信持股率 常續性利益 資料代碼 shares per cscfo cscfi r307 r19 open_d 科目 流通在外股數 本益比 營運現金流 投資現金流 每股淨值 近12月每股營收(元) 開盤價

Python 資料整理程式碼展示

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 tej api 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 start_date = '2015-01-01' end_date = '2025-06-30' pool = get_universe(start = start_date, end = end_date, mkt_bd_e = ['TSE', 'OTC', 'TIB'], # 已上市之股票 stktp_e = 'Common Stock') # 普通股 columns = ['Industry', 'close_d', 'adjfac', 'fld005', 'qfii_pct' , 'fd_pct' , 'ri' , 'shares','per' , 'cscfo' , 'cscfi' , 'r307' , 'r19' , 'open_d', 'pbr', 'psr_tej'] start_dt = pd.Timestamp(start_date, tz = 'UTC') end_dt = pd.Timestamp(end_date, tz = "UTC") data = TejToolAPI.get_history_data(start = start_dt, end = end_dt, ticker = pool, columns = columns, transfer_to_chinese = True) data = data.sort_values('日期') # 依股票代碼進行時間序列運算 data['one_year_max_price'] = data.groupby('股票代碼')['收盤價'] \ .transform(lambda x: x.rolling(window=252, min_periods=1).max()) data['one_year_price'] = data.groupby('股票代碼')['收盤價'] \ .transform(lambda x: x.shift(252)) data['insider_holding_change_6m'] = data.groupby('股票代碼')['董監持股%'] \ .transform(lambda x: x - x.shift(126)) data['foreign_holding_change_6m'] = data.groupby('股票代碼')['外資持股率'] \ .transform(lambda x: x - x.shift(126)) data['fund_holding_change_6m'] = data.groupby('股票代碼')['投信持股率'] \ .transform(lambda x: x - x.shift(126)) # 自由現金流與估值比率 data['FCF'] = data['營運產生現金流量_Q'] - data['投資產生現金流量_Q'] data['Price_FCF'] = data['收盤價'] / data['FCF'] # 注意:這裡假設 '股價淨值比' 是 PBR,如有誤請調整 data['Price_pbr'] = data['收盤價'] / data['股價淨值比'] # 價格營收比 data['Price_Rev'] = data['收盤價'] / data['股價營收比_TEJ'] # 依產業計算平均本益比 data['industry_pe_avg'] = data.groupby('主產業別_中文')['本益比'] \ .transform('mean') data_use = data.copy()

Python 選股邏輯以及回測架構

def compute_growth_strategy(date, data): df = data[data['日期'] == pd.to_datetime(date)].reset_index(drop=True) # 技術面條件:收盤價 > 一年內最高價 50% tech_pass = set(df[df['收盤價'] < 0.5 * df['one_year_max_price']]['股票代碼']) # 董事經理人條件 df['insider_6m_ago'] = df.groupby('股票代碼')['董監持股%'].shift(126) df['insider_growth'] = df['董監持股%'] - df['insider_6m_ago'] insider_pass = set(df[(df['insider_growth'] >= 5) | (df['董監持股%'] >= 10)]['股票代碼']) # 投信條件(附加條件) df['fund_6m_ago'] = df.groupby('股票代碼')['投信持股率'].shift(126) fund_pass = set(df[df['投信持股率'] >= df['fund_6m_ago']]['股票代碼']) # 基本面條件(如果內部人或投信有持股,才檢查基本面) df['cond_A'] = df['本益比'] < 0.85 * df['industry_pe_avg'] df['cond_B'] = df['Price_FCF'] < 1 df['cond_C'] = df['Price_pbr'] < 0.8 df['cond_D'] = df['Price_Rev'] < 1 df['fundamental_score'] = df[['cond_A', 'cond_B', 'cond_C', 'cond_D']].sum(axis=1) df['fundamental_pass'] = df['fundamental_score'] >= 2 # 只對「內部人 or 投信」有動作的股票檢查基本面 trigger_set = insider_pass | fund_pass fundamental_checked_set = set(df[df['股票代碼'].isin(trigger_set) & df['fundamental_pass']]['股票代碼']) # 最終篩選:股價增長 + (內部人或投信) + 基本面 final_selection = tech_pass & trigger_set & fundamental_checked_set print(f"下單日期:{date}, 股價年增長: {len(tech_pass)}, 董監條件: {len(insider_pass)}, 投信條件: {len(fund_pass)}, 觸發基本面: {len(trigger_set)}, 基本面通過: {len(fundamental_checked_set)}, 最終選股: {len(final_selection)}") return final_selection pools = pool + ['IR0001'] start_ingest = start_date.replace('-', '') end_ingest = end_date.replace('-', '') print(f'開始匯入回測資料') simple_ingest(name = 'tquant' , tickers = pools , start_date = start_ingest , end_date = end_ingest) print(f'結束匯入回測資料')

TQuant Lab 回測邏輯程式碼展示

def initialize(context, re = 120): set_slippage(slippage.VolumeShareSlippage(volume_limit=1, price_impact=0.01)) set_commission(commission.Custom_TW_Commission()) set_benchmark(symbol('IR0001')) context.i = 0 context.state = False context.order_tickers = [] context.last_tickers = [] context.rebalance = re def handle_data_1(context, data): # 避免前視偏誤,在篩選股票下一交易日下單 if context.state == True: for i in context.last_tickers: if i not in context.order_tickers: order_target_percent(symbol(i), 0) for i in context.order_tickers: order_target_percent(symbol(i), 1.0 / len(context.order_tickers)) print(f"下單日期:{data.current_dt.date()}, 擇股股票數量:{len(context.order_tickers)}, Leverage: {context.account.leverage}") context.last_tickers = context.order_tickers.copy() context.state = False backtest_date = data.current_dt.date() if context.i % context.rebalance == 0: context.state = True context.order_tickers = compute_growth_strategy(date = backtest_date, data = data_use) record(tickers = context.order_tickers) record(Leverage = context.account.leverage) if context.account.leverage > 1.2: print(f'{data.current_dt.date()}: Over Leverage, Leverage: {round(context.account.leverage, 2)}') for i in context.order_tickers: order_target_percent(symbol(i), 1 / len(context.order_tickers)) context.i += 1 def analyze(context, perf): print(perf.columns) plt.style.use('ggplot') # 第一張圖:策略績效與報酬 fig1, axes1 = plt.subplots(nrows=2, ncols=1, figsize=(18, 10), sharex=False) axes1[0].plot(perf.index, perf['algorithm_period_return'], label='Strategy') axes1[0].plot(perf.index, perf['benchmark_period_return'], label='Benchmark') axes1[0].bar(perf.index, perf['algorithm_period_return'] - perf['benchmark_period_return'], label='Excess return', color='g', alpha=0.4) axes1[0].set_title("Backtest Results") axes1[0].legend() axes1[1].plot(perf.index, perf['benchmark_volatility'], label='Benchmark Volatility') axes1[1].plot(perf.index, perf['algo_volatility'], label='Strategy Volatility') axes1[1].set_title("Voloatility") axes1[1].legend() plt.tight_layout() plt.show() results = run_algorithm( start = pd.Timestamp('2018-01-01', tz = 'utc'), end = pd.Timestamp(end_date, tz = 'utc'), initialize = initialize, handle_data = handle_data_1, analyze = analyze, bundle = 'tquant', capital_base = 1e5)

安東尼·賣利亞 的反向市場致勝術

績效指標 / 策略 大盤(Benchmark) 本投資策略 年化報酬率 13.91% 14.53% 累積報酬率 156.29% 166.60% 年化波動度 17.54% 23.93% 夏普值 0.83 0.69 卡瑪比率 0.51 0.33 期間最大回撤 -27.37% -43.47%

從回測結果來看,本投資策略在 年化報酬率(14.53%)與累積報酬率(166.6%) 上略優於大盤(13.91% / 156.29%),但整體風險調整後的表現並不理想。策略的 年化波動度高達 23.93%,遠高於大盤的 17.54%,導致 夏普值僅為 0.69,低於大盤的 0.83,代表單位風險所帶來的報酬反而較差。此外,最大回撤高達 -43.47%,相較於大盤的 -27.37%,顯示該策略在市場波動時缺乏足夠的防禦能力。

策略的 卡瑪比率為 0.33,也低於大盤的 0.51,意味著其承受每一單位最大損失所換得的報酬相對偏低,這點在圖中「Excess return」長期為負的部分亦可見端倪,特別是在 2021~2024 年間策略表現明顯落後。

完整程式碼連結

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

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

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

點我註冊會員,開始試用

延伸閱讀

從景氣燈號到資產輪動:一套避開熊市的量化策略

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

查爾士.布蘭帝 價值型選股法則:打造安全邊際的投資組合

相關連結

查看原始文章

更多理財相關文章

01

她退休11年總花費只有314萬!居無定所走到哪、玩到哪、住到哪,壯遊世界把旅行當生活

幸福熟齡 X 今周刊
02

年薪破300萬!黃仁勳點未來搶手「3職業」成金飯碗:寫程式不是唯一出路

三立新聞網
03

破天荒!台北101「15.1%股權」標售 最低5.7億當「國家門面」房東

太報
04

賣藍莓先看台灣!外媒曝市場的秘密

自由電子報
05

政府打房建商慘1/新舊青安累計撥款2.55兆 兩顆未爆彈全民挫咧等

鏡週刊
06

「陶朱隱園」沒有蓋牌! 17樓見實登「單價飆364萬」

ETtoday新聞雲
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...