Portfolio Dividend Yield: Weighted Average
How to calculate portfolio dividend yield: total annual income divided by market value, plus why averaging the yield column misleads and what fixes it.
Your portfolio dividend yield is one number: the total annual dividend income your holdings are set to pay, divided by the total market value of those holdings. Written the other way round, it is a market-value-weighted average of the individual yields, where each holding's weight is its dollar value as a fraction of the whole. The average of the yield column on a brokerage screen is a different number, and in most real portfolios it is the flattering one.
How to calculate portfolio dividend yield
For each position, multiply shares by the annual dividend per share. That gives the position's annual income in dollars. Add those incomes together, divide by the sum of shares times price across every position, and multiply by 100. That is the whole calculation, for four holdings or forty.
There are two equivalent ways to write it:
- Total annual income divided by total portfolio market value.
- The sum, across holdings, of weight multiplied by yield, where weight is position value divided by portfolio value.
The second form shows where a simple average goes wrong. Every holding appears in both versions, but in the correct one each carries the weight of its dollars rather than one vote apiece. Cash you count as part of the portfolio belongs in the denominator too. Leaving it out lifts the printed yield without adding a cent of income. For the single-stock version of the arithmetic, start with how to calculate dividend yield.
Why averaging the yield column overstates your income
The gap between the two methods is easiest to see in a worked example. The script below is plain Python 3 with nothing imported. Any machine with python3 runs it. The five positions are hypothetical round numbers, chosen to make the effect obvious: a name, a share count, a price, and an annual dividend per share.
#!/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}%'
)
Run it and the two headline lines disagree sharply. The portfolio holds $40,000 of stock and pays $1,070 a year, a weighted yield of 2.68%. The simple average of the five yield figures prints 4.00%, roughly one and a half times as much.
Position E is the whole story. It yields 9.00%, the highest in the book, on $1,000 of stock. That is 2.5% of the portfolio's value and 8.4% of its income. In the simple average it gets one fifth of the vote, the same as the $15,000 position sitting next to it. A yield figure only means something once you attach the dollars standing behind it.
What weighting looks like across real holdings
Household dividend payers spread across a wide range of yields, which is what makes the weighting choice matter. The panel below computes each name's trailing yield from the last twelve months of declared dividends per share, divided by the most recent daily close.
The exact SQL behind every number
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 DESCAcross these 10 names, priced through Aug 7, 2026, the yields run from 5.96% at VZ down to 0.34% at AAPL. A portfolio built out of a spread like this one lands nowhere near the midpoint of the column unless the positions happen to be equal in dollars.
Now put five of them into a portfolio with fixed share counts and read two numbers side by side: each holding's share of market value, and its share of the annual income.
The exact SQL behind every number
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 DESCThe pattern from the script repeats on live prices. MSFT is the largest position at 31.4% of market value while supplying 10% of the income, on a yield of 0.72%. At the other end, VZ holds 5.9% of the value and pays 15.7% of the income, on 5.96%. Weight and income share come apart whenever the yields differ.
Averaged the two ways, the same five holdings give two different answers.
The exact SQL behind every number
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 DESCThe higher line, at 2.93%, is the Simple average of the five yields figure. The other prints 2.24%. Only one of them corresponds to cash. Multiply the weighted figure by the portfolio's market value and you recover the annual income. Do the same with the simple average and you get a number no statement will ever show. The direction is structural: when the higher-yielding names are the smaller positions, the simple average sits above the true portfolio yield, and when the high yielders are the big positions it sits below.
Yield on cost is not portfolio yield
Yield on cost divides the current annual dividend by the price you paid, not by what the position is worth today. A position bought at $20 that now trades at $80, still paying $1.00 a share a year, shows a yield on cost of 5.00% against a current yield of 1.25%. The dividend is the same dollar. Only the denominator moved.
Both numbers are legitimate, and they answer different questions. Current market value in the denominator tells you what the portfolio yields today, the figure comparable to a good dividend yield or to a Treasury bill. Cost basis in the denominator tells you about a purchase you made in the past. The trouble starts when the two get mixed inside one portfolio total: current yield on the recent buys, yield on cost on the ones held for a decade. That blend routinely doubles the apparent yield of an income portfolio and matches nothing a broker reports. Dividend yield traps covers the opposite illusion, a yield that prints high after a steep price decline.
Trailing or forward inputs change the answer
Every figure above depends on which dividend number goes into the numerator. Trailing yield sums the last twelve months of payments actually declared. Forward yield annualizes the most recent payment, usually the latest quarterly amount multiplied by four. The two disagree on any name that changed its payout or paid a special distribution during the year, and one large holding can move the portfolio total by a meaningful amount on its own. The panels here use trailing inputs. Trailing vs forward dividend yield sets out when each one is the right choice.
The yield is a rate, the cash arrives in lumps
A weighted yield is an annual rate, not a payment schedule. The same five positions pay out across a handful of clustered months, since most US companies go ex-dividend quarterly and land on similar calendars.
The exact SQL behind every number
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 monthCash landed in 12 distinct months over the trailing year, the most recent of them Jul 2026 at $107.76. A portfolio yield of a few percent says nothing about which month the money shows up in, which is why an income plan built on a monthly figure needs the calendar alongside the rate. For the market-wide level these portfolio numbers get measured against, see the S&P 500 dividend yield.
FAQ
How do I calculate the dividend yield of my whole portfolio?
Add up each holding's annual dividend income (shares multiplied by the annual dividend per share), add up each holding's market value (shares multiplied by price), then divide total income by total value and multiply by 100. The result is the market-value-weighted average of your holdings' yields.
Why is the average of my holdings' yields higher than my portfolio yield?
A simple average gives every position one vote. A portfolio yield gives every position a vote proportional to its dollars. When the highest-yielding names are your smallest positions, the simple average lands above the yield the portfolio actually pays, sometimes by half again as much.
Does cash count in a portfolio dividend yield?
If you treat cash as part of the portfolio, it belongs in the denominator with a zero dividend, and it lowers the yield. Excluding it reports the yield of the invested sleeve only. Either convention works as long as you apply the same one every time you measure.
Is yield on cost the same as my portfolio's yield?
No. Yield on cost divides today's dividend by the price you paid years ago, so it climbs whenever the stock does. Portfolio yield uses current market value, which is the figure that ties back to the income your current holdings generate.
Does adding one high-yield stock lift my portfolio yield much?
Only in proportion to the dollars in it. A 9% yielder held at 2.5% of the portfolio adds roughly 0.2 percentage points to the weighted yield (0.09 multiplied by 0.025), whatever it does to the average of the yield column.
Every panel here carries the exact SQL that produced it, so the weighting is auditable line by line. To run the same calculation over your own list of names and share counts, ask for it in plain English on the Strasmore terminal.