投資組合殖利率怎麼算:市值加權平均
了解投資組合殖利率的正確算法:用年度股息收入除以總市值,並看懂直接平均殖利率欄為何會高估收入,附 Python 實例。
投資組合殖利率是一個單一數字:持股預計支付的年度股息收入總額,除以這些持股的總市值。換個方式表示,它是各持股個別殖利率的市值加權平均,其中每檔持股的權重,是其美元價值占整體投資組合的比例。券商畫面上殖利率欄的平均值則是另一個數字,而且在多數真實投資組合中,通常是看起來較漂亮的那一個。
如何計算投資組合殖利率
對每個部位,將持有股數乘以每股年度股息。所得就是該部位的年度美元收入。將所有部位的收入加總,再除以所有部位的股數乘以股價後的總和,最後乘以 100。無論持有四檔或四十檔,計算方式都相同。
這可用兩種等價方式表示:
- 年度收入總額除以投資組合市值總額。
- 將各持股的權重乘以殖利率後加總;權重為部位價值除以投資組合價值。
第二種形式顯示簡單平均的問題所在。兩種計算都納入每一檔持股,但正確計算方式是按照其美元價值加權,而不是每檔各有一票。若你將現金視為投資組合的一部分,也應將其計入分母。排除現金會在沒有增加任何收入的情況下,提高顯示的殖利率。若要了解單一股票的計算方式,請從如何計算股息殖利率開始。
為何平均殖利率欄會高估你的收入
兩種方法之間的差異,透過以下實例最容易看出。下方程式碼是未匯入任何套件的標準 Python 3。任何能執行 python3 的電腦都可以運行。五檔持股採用假設性的整數數字,目的是清楚呈現差異:股票名稱、持有股數、股價及每股年度股息。
#!/usr/bin/env python3
# each row: (name, shares, price, annual dividend per share)
holdings = [
('A', 200, 50.00, 1.50),
('B', 150, 100.00, 2.00),
('C', 100, 80.00, 0.80),
('D', 300, 20.00, 1.00),
('E', 100, 10.00, 0.90),
]
total_value = sum(sh * px for _, sh, px, _ in holdings)
total_income = sum(sh * dps for _, sh, _, dps in holdings)
naive = sum(100 * dps / px for _, _, px, dps in holdings) / len(holdings)
print(f'market value : ${total_value:,.2f}')
print(f'annual income : ${total_income:,.2f}')
print(f'portfolio yield : {100 * total_income / total_value:.2f}%')
print(f'average of yields : {naive:.2f}%')
for name, sh, px, dps in holdings:
value = sh * px
income = sh * dps
print(
f'{name} weight {100 * value / total_value:5.1f}%'
f' yield {100 * dps / px:5.2f}%'
f' income ${income:8,.2f}'
f' share of income {100 * income / total_income:5.1f}%'
)
執行後,兩個主要數字會出現明顯差異。投資組合持有價值 40,000 美元的股票,每年支付 1,070 美元;加權殖利率為 2.68%。五檔股票殖利率的簡單平均為 4.00%,約為前者的一點五倍。
E 檔持股完整呈現了問題。其殖利率為 9.00%,是整個投資組合中最高,但股票價值僅 1,000 美元。這相當於投資組合市值的 2.5%,以及收入的 8.4%。在簡單平均中,它卻取得五分之一的權重,與旁邊價值 15,000 美元的部位相同。殖利率數字必須與其背後代表的美元價值連結,才具有意義。
實際持股中的加權方式
家庭投資人持有的配息股票,其殖利率分布範圍很廣,因此加權方式十分重要。下方面板以過去十二個月已宣告的每股股息總額,除以最近一個交易日的收盤價,計算各檔股票的過去殖利率。
每個數據背後的精確 SQL 語法
WITH
last_px AS
(
SELECT
ticker,
toFloat64(argMax(close, date)) AS px,
formatDateTime(max(date), '%b %e, %Y') AS priced_through
FROM global_markets.stocks_daily_aggs
WHERE ticker IN ('AAPL', 'MSFT', 'HD', 'KO', 'PG', 'PEP', 'JNJ', 'XOM', 'CVX', 'VZ')
AND date >= today() - 30
GROUP BY ticker
),
ttm_dps AS
(
SELECT
ticker,
sum(amount) AS dps
FROM
(
SELECT
ticker,
id,
toFloat64(any(cash_amount)) AS amount
FROM global_markets.stocks_dividends
WHERE ticker IN ('AAPL', 'MSFT', 'HD', 'KO', 'PG', 'PEP', 'JNJ', 'XOM', 'CVX', 'VZ')
AND ex_dividend_date > today() - 365
AND ex_dividend_date <= today()
GROUP BY ticker, id
)
GROUP BY ticker
)
SELECT
p.ticker AS symbol,
round(d.dps, 2) AS ttm_dividend_usd,
round(100 * d.dps / p.px, 2) AS yield_pct,
p.priced_through AS priced_through
FROM last_px AS p
INNER JOIN ttm_dps AS d ON d.ticker = p.ticker
ORDER BY yield_pct DESC在這 10 檔股票中,股價介於 Aug 7, 2026,殖利率則從 5.96% 的 VZ,一路降至 0.34% 的 AAPL。若投資組合由這種殖利率分布的股票組成,只要各部位的美元價值不相等,其整體殖利率就不會接近殖利率欄的中點。
接著,從中選出五檔股票,以固定股數建立投資組合,並將兩個數字並列比較:各持股占市值的比例,以及各持股占年度收入的比例。
每個數據背後的精確 SQL 語法
WITH
positions AS
(
SELECT
tupleElement(p, 1) AS ticker,
toFloat64(tupleElement(p, 2)) AS shares
FROM
(
SELECT arrayJoin([('MSFT', 30.), ('PG', 60.), ('XOM', 80.), ('KO', 100.), ('VZ', 60.)]) AS p
)
),
last_px AS
(
SELECT
ticker,
toFloat64(argMax(close, date)) AS px
FROM global_markets.stocks_daily_aggs
WHERE ticker IN ('MSFT', 'PG', 'XOM', 'KO', 'VZ')
AND date >= today() - 30
GROUP BY ticker
),
ttm_dps AS
(
SELECT
ticker,
sum(amount) AS dps
FROM
(
SELECT
ticker,
id,
toFloat64(any(cash_amount)) AS amount
FROM global_markets.stocks_dividends
WHERE ticker IN ('MSFT', 'PG', 'XOM', 'KO', 'VZ')
AND ex_dividend_date > today() - 365
AND ex_dividend_date <= today()
GROUP BY ticker, id
)
GROUP BY ticker
),
holding AS
(
SELECT
pos.ticker AS ticker,
pos.shares * lp.px AS value_usd,
pos.shares * d.dps AS income_usd
FROM positions AS pos
INNER JOIN last_px AS lp ON lp.ticker = pos.ticker
INNER JOIN ttm_dps AS d ON d.ticker = pos.ticker
)
SELECT
h.ticker AS symbol,
round(100 * h.value_usd / t.total_value, 1) AS weight_pct,
round(100 * h.income_usd / t.total_income, 1) AS income_share_pct,
round(100 * h.income_usd / h.value_usd, 2) AS yield_pct
FROM holding AS h
CROSS JOIN
(
SELECT
sum(value_usd) AS total_value,
sum(income_usd) AS total_income
FROM holding
) AS t
ORDER BY weight_pct DESC實際股價再次呈現程式中的模式。MSFT 是最大部位,占市值 31.4%,同時提供 10% 的收入,殖利率為 0.72%。在另一端,VZ 僅占市值 5.9%,卻提供 15.7% 的收入,殖利率為 5.96%。只要各持股的殖利率不同,市值權重與收入占比就會出現差異。
以兩種方式分別計算平均後,同樣的五檔持股會得到兩個不同答案。
每個數據背後的精確 SQL 語法
WITH
positions AS
(
SELECT
tupleElement(p, 1) AS ticker,
toFloat64(tupleElement(p, 2)) AS shares
FROM
(
SELECT arrayJoin([('MSFT', 30.), ('PG', 60.), ('XOM', 80.), ('KO', 100.), ('VZ', 60.)]) AS p
)
),
last_px AS
(
SELECT
ticker,
toFloat64(argMax(close, date)) AS px
FROM global_markets.stocks_daily_aggs
WHERE ticker IN ('MSFT', 'PG', 'XOM', 'KO', 'VZ')
AND date >= today() - 30
GROUP BY ticker
),
ttm_dps AS
(
SELECT
ticker,
sum(amount) AS dps
FROM
(
SELECT
ticker,
id,
toFloat64(any(cash_amount)) AS amount
FROM global_markets.stocks_dividends
WHERE ticker IN ('MSFT', 'PG', 'XOM', 'KO', 'VZ')
AND ex_dividend_date > today() - 365
AND ex_dividend_date <= today()
GROUP BY ticker, id
)
GROUP BY ticker
),
holding AS
(
SELECT
pos.shares * lp.px AS value_usd,
pos.shares * d.dps AS income_usd
FROM positions AS pos
INNER JOIN last_px AS lp ON lp.ticker = pos.ticker
INNER JOIN ttm_dps AS d ON d.ticker = pos.ticker
)
SELECT
tupleElement(m, 1) AS method,
round(tupleElement(m, 2), 2) AS yield_pct
FROM
(
SELECT arrayJoin([
('Market-value weighted portfolio yield', weighted_yield),
('Simple average of the five yields', naive_yield)
]) AS m
FROM
(
SELECT
100 * sum(income_usd) / sum(value_usd) AS weighted_yield,
avg(100 * income_usd / value_usd) AS naive_yield
FROM holding
)
)
ORDER BY yield_pct DESC較高的數字 2.93% 是 Simple average of the five yields。另一個數字為 2.24%。只有其中一個與現金收入相符。將加權殖利率乘以投資組合市值,就能還原年度收入。若對簡單平均採取相同計算,所得數字不會出現在任何一份對帳單上。這種方向是由結構決定的:當高殖利率股票的部位較小時,簡單平均會高於投資組合的真實殖利率;當高殖利率股票的部位較大時,簡單平均則會低於真實殖利率。
成本殖利率不是投資組合殖利率
成本殖利率是以目前年度股息除以買進價格,而不是除以目前部位價值。若一個部位以 20 美元買進,目前股價為 80 美元,每年仍支付每股 1.00 美元,則其成本殖利率為 5.00%,目前殖利率則為 1.25%。股息金額相同,只有分母發生變化。
兩個數字都合理,但回答的是不同問題。以目前市值為分母,能告訴你投資組合在今日的殖利率;這也是可與良好的股息殖利率或國庫券比較的數字。以成本基礎為分母,則反映你過去進行的一筆買進交易。問題始於將兩者混用於同一個投資組合總計中:近期買進的部位使用目前殖利率,持有十年的部位使用成本殖利率。這種混合方式經常使收益型投資組合的表面殖利率翻倍,而且與券商報告的任何數字都不相符。股息殖利率陷阱說明另一種相反的錯覺,也就是股價大幅下跌後顯示出很高的殖利率。
過去殖利率或預期殖利率會改變答案
上述每個數字,都取決於分子採用哪一個股息數字。過去殖利率加總最近十二個月實際宣告的股息。預期殖利率則將最近一次股息年化,通常是將最新一季的股息乘以四。只要股票曾調整股息,或在年度內發放特別股息,兩者就會出現差異;而單一大型持股本身就可能對投資組合總計造成顯著影響。本文各面板均採用過去股息數據。過去殖利率與預期股息殖利率說明何時應採用其中一種。
殖利率是比率,現金則分批到帳
加權殖利率是年度比率,不是付款時程。同樣的五檔持股會在幾個集中月份支付股息,因為多數美國公司按季除息,除息日通常落在相近的時間表上。
每個數據背後的精確 SQL 語法
WITH
positions AS
(
SELECT
tupleElement(p, 1) AS ticker,
toFloat64(tupleElement(p, 2)) AS shares
FROM
(
SELECT arrayJoin([('MSFT', 30.), ('PG', 60.), ('XOM', 80.), ('KO', 100.), ('VZ', 60.)]) AS p
)
),
payments AS
(
SELECT
ticker,
id,
any(ex_dividend_date) AS ex_date,
toFloat64(any(cash_amount)) AS amount
FROM global_markets.stocks_dividends
WHERE ticker IN ('MSFT', 'PG', 'XOM', 'KO', 'VZ')
AND ex_dividend_date > today() - 365
AND ex_dividend_date <= today()
GROUP BY ticker, id
)
SELECT
formatDateTime(toStartOfMonth(pay.ex_date), '%Y-%m') AS month,
formatDateTime(toStartOfMonth(pay.ex_date), '%b %Y') AS month_label,
round(sum(pos.shares * pay.amount), 2) AS income_usd
FROM payments AS pay
INNER JOIN positions AS pos ON pos.ticker = pay.ticker
GROUP BY month, month_label
ORDER BY month在過去一年中,現金分別於 12 個月份入帳;最近一次是在 Jul 2026,金額為 $107.76。幾個百分點的投資組合殖利率,無法告訴你資金會在哪個月份入帳。因此,以月度數字制定收入計畫時,也需要同時掌握付款日曆。若要了解這些投資組合數字所比較的整體市場水準,請參閱S&P 500 殖利率。
常見問題
如何計算整個投資組合的股息殖利率?
將各持股的年度股息收入加總,即持有股數乘以每股年度股息;再將各持股的市值加總,即持有股數乘以股價。接著以總收入除以總市值,再乘以 100。所得結果就是各持股殖利率的市值加權平均。
為何各持股殖利率的平均值高於投資組合殖利率?
簡單平均給每個部位一票。投資組合殖利率則按照各部位的美元價值給予相應權重。當殖利率最高的股票是你的最小部位時,簡單平均就會高於投資組合實際產生的殖利率,有時甚至高出一半。
現金是否計入投資組合股息殖利率?
若你將現金視為投資組合的一部分,應將其以零股息計入分母,這會降低殖利率。排除現金,則代表只計算已投資部位的殖利率。兩種方法都可以,但每次計算時必須採用相同標準。
成本殖利率是否等於投資組合殖利率?
不是。成本殖利率是以今日股息除以多年前的買進價格,因此股價上漲時,成本殖利率也會提高。投資組合殖利率則採用目前市值,這才是能與目前持股所產生收入連結的數字。
加入一檔高殖利率股票,會大幅提高投資組合殖利率嗎?
只會按照該股票在投資組合中的美元價值比例產生影響。一檔殖利率為 9%、占投資組合 2.5% 的股票,會使加權殖利率增加約 0.2 個百分點(0.09 乘以 0.025),至於殖利率欄的平均值如何變化則是另一回事。
本文每個面板都附有產生該結果的完整 SQL,因此每一行的加權方式都可以查核。若要對你自己的股票清單與持有股數執行相同計算,請在 Strasmore terminal 以白話提出需求。