Strasmore Research
Learn am Matt ConnorBy Matt Connor · Updated 2026-07-25

how dem dey measure monthly stock return

Monthly stock return depend on which two prices you pick, if dividends dey count, and if dem adjust for splits. We run all di conventions on top real market data make you no confuse.

If you ask wetin one stock "do for June", you go see different numbers from different sources — and all of dem correct. Monthly stock return na something wey dey defined by choices wey plenty people no dey see: which two prices mark the start and end of the month, whether dividends dey count, and whether dem adjust the price series for splits. This page dey run the different conventions on top real market data, side by side, so the differences no go confuse you again.

Close-to-close: di standard convention

Di convention wey index providers, fund fact sheets, and most quote pages dey use: di month return dey run from di prior month final regular-session closing price go reach dis month final close. May last close na di starting line for June. Di arithmetic na just one line — ending price minus starting price, divide am by starting price. Make we try small example first: one stock wey end May for $50.00 and end June for $53.50 return (53.50 − 50.00) ÷ 50.00 = 0.07, or 7%, for di month. Every convention for dis page dey feed different inputs inside dat same formula. Dis na how e be for di real June 2026 tape, side-by-side with di main alternative:

QueryJune 2026 two ways — close-to-close vs open-to-close, NVDA and SPY
The exact SQL behind every number
SELECT ticker,
       round(argMaxIf(cl, day, day <= '2026-05-31'), 2) AS may_final_close,
       round(argMinIf(op, day, day >= '2026-06-01'), 2) AS june_first_open,
       round(argMaxIf(cl, day, day <= '2026-06-30'), 2) AS june_final_close,
       round(100 * (argMaxIf(cl, day, day <= '2026-06-30') - argMaxIf(cl, day, day <= '2026-05-31'))
             / argMaxIf(cl, day, day <= '2026-05-31'), 2) AS close_to_close_pct,
       round(100 * (argMaxIf(cl, day, day <= '2026-06-30') - argMinIf(op, day, day >= '2026-06-01'))
             / argMinIf(op, day, day >= '2026-06-01'), 2) AS open_to_close_pct,
       round(abs(100 * (argMaxIf(cl, day, day <= '2026-06-30') - argMinIf(op, day, day >= '2026-06-01'))
                 / argMinIf(op, day, day >= '2026-06-01')
             - 100 * (argMaxIf(cl, day, day <= '2026-06-30') - argMaxIf(cl, day, day <= '2026-05-31'))
                 / argMaxIf(cl, day, day <= '2026-05-31')), 2) AS convention_gap_points
FROM (
    SELECT ticker,
               toDate(toTimeZone(window_start, 'America/New_York')) AS day,
               argMinIf(toFloat64(open), window_start, rth) AS op,
               argMaxIf(toFloat64(close), window_start, rth) AS cl
        FROM (
            SELECT ticker, window_start, open, close,
                   toTimeZone(window_start, 'America/New_York') >= toDateTime(concat(toString(toDate(toTimeZone(window_start, 'America/New_York'))), ' 09:30:00'), 'America/New_York')
                   AND toTimeZone(window_start, 'America/New_York') < toDateTime(concat(toString(toDate(toTimeZone(window_start, 'America/New_York'))), ' 16:00:00'), 'America/New_York') AS rth
            FROM global_markets.delayed_stocks_minute_aggs
            WHERE ticker IN ('NVDA', 'SPY')
              AND window_start >= '2026-05-28 04:00:00'
              AND window_start < '2026-07-01 08:00:00'
        )
        GROUP BY ticker, day
)
GROUP BY ticker
ORDER BY ticker

NVDA June na -5.39% close-to-close — but -7.4% measure from June first regular-session open go reach im last close. Same stock, same month, 2.01 percentage points dey between di two honest answers. Di whole difference na one overnight+weekend window: NVDA close May for $211.15 and open June for $215.73 — dat jump belong to "June" for di open-to-close convention and to di boundary for close-to-close. SPY show di same anatomy more gently: -1.33% close-to-close versus -1.2% open-to-close.

No number dey wrong. Close-to-close dey answer "wetin continuing holder experience across di month boundary?" Open-to-close dey answer "wetin di month own trading deliver to person wey buy at di June open?" Dem dey differ anytime market gap across di boundary — and overnight gaps dey routine, no be rare.

Dividends: price return versus total return

Di second choice: di cash wey one stock pay out during di month, e dey count? Price return no dey look am; total return dey add am back. For most single months, di gap dey small — one stock wey dey pay quarterly dey share roughly one-quarter of im yield per quarter — but e dey compound into big difference over years, and June 2026 just happen be SPY payment month:

QuerySPY June 2026 — price return vs total return (June ex-dividend dey inside)
The exact SQL behind every number
WITH px AS (
    SELECT argMaxIf(cl, day, day <= '2026-05-31') AS may_close,
           argMaxIf(cl, day, day <= '2026-06-30') AS jun_close
    FROM (
        SELECT ticker,
               toDate(toTimeZone(window_start, 'America/New_York')) AS day,
               argMinIf(toFloat64(open), window_start, rth) AS op,
               argMaxIf(toFloat64(close), window_start, rth) AS cl
        FROM (
            SELECT ticker, window_start, open, close,
                   toTimeZone(window_start, 'America/New_York') >= toDateTime(concat(toString(toDate(toTimeZone(window_start, 'America/New_York'))), ' 09:30:00'), 'America/New_York')
                   AND toTimeZone(window_start, 'America/New_York') < toDateTime(concat(toString(toDate(toTimeZone(window_start, 'America/New_York'))), ' 16:00:00'), 'America/New_York') AS rth
            FROM global_markets.delayed_stocks_minute_aggs
            WHERE ticker IN ('NVDA', 'SPY')
              AND window_start >= '2026-05-28 04:00:00'
              AND window_start < '2026-07-01 08:00:00'
        )
        GROUP BY ticker, day
    )
    WHERE ticker = 'SPY'
),
dv AS (
    SELECT sum(cash_amount) AS div_per_share
    FROM global_markets.stocks_dividends
    WHERE ticker = 'SPY'
      AND ex_dividend_date >= '2026-06-01'
      AND ex_dividend_date <= '2026-06-30'
)
SELECT round(may_close, 2) AS may_final_close,
       round(jun_close, 2) AS june_final_close,
       round(div_per_share, 4) AS june_dividend_per_share,
       round(100 * (jun_close - may_close) / may_close, 2) AS price_return_pct,
       round(100 * div_per_share / may_close, 2) AS dividend_contribution_pct,
       round(100 * ((jun_close + div_per_share) - may_close) / may_close, 2) AS total_return_pct
FROM px CROSS JOIN dv

SPY June price return na -1.33%; add di $1.9035 per-share distribution wey go ex-dividend during di month and di total return improve to -1.08% — na 0.25% contribution from cash. Index families dey publish both flavors (di "S&P 500" wey you dey see quoted na usually di price index), fund performance pages must show total return, and screeners dey mix di two anyhow. When two sources no gree by few tenths for one dividend payer month, dividend na di first suspect — di ex-dividend date na im dey determine which month one payment belong to.

One single June na just tenths of percent. Horizon na wetin dey separate di two conventions — check dis same arithmetic wey dem stretch across five years:

QuerySPY for five years, June 2021 to June 2026 — price return vs return wey get dividends
The exact SQL behind every number
WITH daily AS (
    SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS day,
           argMaxIf(toFloat64(close), window_start,
               toTimeZone(window_start, 'America/New_York') >= toDateTime(concat(toString(toDate(toTimeZone(window_start, 'America/New_York'))), ' 09:30:00'), 'America/New_York')
               AND toTimeZone(window_start, 'America/New_York') < toDateTime(concat(toString(toDate(toTimeZone(window_start, 'America/New_York'))), ' 16:00:00'), 'America/New_York')) AS cl
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker = 'SPY'
      AND ((window_start >= '2021-06-28 04:00:00' AND window_start < '2021-07-01 08:00:00')
        OR (window_start >= '2026-06-26 04:00:00' AND window_start < '2026-07-01 08:00:00'))
    GROUP BY day
),
px AS (
    SELECT argMaxIf(cl, day, day <= '2021-06-30') AS start_close,
           argMaxIf(cl, day, day <= '2026-06-30') AS end_close
    FROM daily
),
dv AS (
    SELECT round(sum(cash_amount), 2) AS divs_per_share,
           count() AS payments
    FROM global_markets.stocks_dividends
    WHERE ticker = 'SPY'
      AND ex_dividend_date >= '2021-07-01'
      AND ex_dividend_date <= '2026-06-30'
)
SELECT round(start_close, 2) AS june_2021_close,
       round(end_close, 2) AS june_2026_close,
       divs_per_share AS dividends_paid_per_share,
       payments AS dividend_payments,
       round(100 * (end_close - start_close) / start_close, 2) AS five_year_price_return_pct,
       round(100 * ((end_close + divs_per_share) - start_close) / start_close, 2) AS five_year_return_with_divs_pct,
       round(100 * divs_per_share / start_close, 2) AS dividend_points_added
FROM px CROSS JOIN dv

From im June 2021 close of $428.07 to im June 2026 close of $746.32, SPY five-year price return measure 74.35%. Over di same period e pay 20 distributions wey total $34.07 per share; counting dat cash lift di figure to 82.3% — na 7.96 points of gap before dem reinvest even one dividend. One true total-return index dey reinvest each payment for im ex-date, and di reinvested shares dey push di gap even wider. For one mature payer, "large margins" no be figure of speech; na di default outcome of years of quarterly cash.

Adjusted close vs. raw close: di split trap

Plenty chart tools — Yahoo Finance, Google Finance, most broker apps — dey quote adjusted close: na historical price series wey dem don divide past prints down for splits (and for some platforms, dem adjust am for dividends too) so di line fit dey comparable through time. Our warehouse dey store di tape as e actually print. For ordinary month, di two dey agree. For month wey get stock split, dem dey tell tori wey no match at all — NVDA 10-for-1 split wey happen for June 10, 2024, inside di month:

QueryNVDA June 2024 — raw close-to-close calc vs di split-adjusted return
The exact SQL behind every number
WITH px AS (
    SELECT argMaxIf(cl, day, day <= '2024-05-31') AS may_close,
           argMaxIf(cl, day, day <= '2024-06-30') AS jun_close
    FROM (
        SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS day,
               argMaxIf(toFloat64(close), window_start,
                   toTimeZone(window_start, 'America/New_York') >= toDateTime(concat(toString(toDate(toTimeZone(window_start, 'America/New_York'))), ' 09:30:00'), 'America/New_York')
                   AND toTimeZone(window_start, 'America/New_York') < toDateTime(concat(toString(toDate(toTimeZone(window_start, 'America/New_York'))), ' 16:00:00'), 'America/New_York')) AS cl
        FROM global_markets.delayed_stocks_minute_aggs
        WHERE ticker = 'NVDA'
          AND window_start >= '2024-05-29 04:00:00'
          AND window_start < '2024-07-01 08:00:00'
        GROUP BY day
    )
),
sp AS (
    SELECT split_to / split_from AS ratio
    FROM global_markets.stocks_splits
    WHERE ticker = 'NVDA'
      AND execution_date >= '2024-06-01'
      AND execution_date <= '2024-06-30'
)
SELECT round(may_close, 2) AS may_final_close_raw,
       round(jun_close, 2) AS june_final_close_raw,
       toInt32(ratio) AS shares_after_split_per_old_share,
       round(100 * (jun_close - may_close) / may_close, 2) AS raw_price_change_pct,
       round(may_close / ratio, 2) AS may_close_split_adjusted,
       round(100 * (jun_close - may_close / ratio) / (may_close / ratio), 2) AS adjusted_return_pct
FROM px CROSS JOIN sp

If you read am raw, NVDA "fall" from $1097.46 go $123.41 dat June — na -88.75% collapse. Nothing like dat happen: for di execution date, each old share turn to 10 new ones and di price divide to match, so every holder dollar value remain where e dey. Divide di May close by di same ratio — $109.75 on split-adjusted basis — and June 2024 measure 12.45%, na up month. Any source wey dey quote adjusted data go report something near di second figure; naive calculation on raw prints go report di first one. Adjusted close na di single most common reason why two sources go disagree well-well on historical monthly return — when figure look too big for either direction, check di split calendar before you do anything else.

Which "close" be di close?

One more boundary dey hide inside di conventions: di closing price itself. Di number wey every fact sheet dey use na di official closing-auction print — di single price wey di closing auction set for 4:00 PM ET. But trading dey continue after hours, and di last print of di extended session fit drift from di official close:

QuerySPY for June 30, 2026 — official-close vs di final extended-hours print
The exact SQL behind every number
SELECT round(argMaxIf(toFloat64(close), window_start, rth), 2) AS regular_session_close,
       round(argMax(toFloat64(close), window_start), 2) AS last_extended_print,
       round(100 * (argMax(toFloat64(close), window_start) - argMaxIf(toFloat64(close), window_start, rth))
             / argMaxIf(toFloat64(close), window_start, rth), 3) AS after_hours_drift_pct
FROM (
    SELECT window_start, close,
           toTimeZone(window_start, 'America/New_York') >= toDateTime('2026-06-30 09:30:00', 'America/New_York')
           AND toTimeZone(window_start, 'America/New_York') < toDateTime('2026-06-30 16:00:00', 'America/New_York') AS rth
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker = 'SPY'
      AND window_start >= '2026-06-30 04:00:00'
      AND window_start < '2026-07-01 08:00:00'
)

For June 30 di two differ by -0.003% — e small dat evening, but for an earnings night for one single stock di extended session fit move percent-scale amounts wey di official monthly return go assign to di next month. After-hours and premarket trading cover dat boundary for detail. One related edge case: month boundaries sometimes land for holiday-shortened session, where di regular day end by 1:00 PM ET and di official close print for early closing auction — di market holiday schedule list dem.

How twelve months dey compound inside one year

One year return no be the sum of twelve monthly returns — na their product. The linking formula: convert each month into growth factor (1 + return), multiply the twelve factors together, then subtract 1. One month wey drop −5% and another wey rise +5% fit sum to zero but dem compound to 0.95 × 1.05 − 1 = −0.25%. See SPY actual monthly path for the twelve months wey end June 2026:

QuerySPY monthly close-to-close returns — twelve months wey end June 2026, simple and log
The exact SQL behind every number
WITH daily AS (
    SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS day,
           argMaxIf(toFloat64(close), window_start,
               toTimeZone(window_start, 'America/New_York') >= toDateTime(concat(toString(toDate(toTimeZone(window_start, 'America/New_York'))), ' 09:30:00'), 'America/New_York')
               AND toTimeZone(window_start, 'America/New_York') < toDateTime(concat(toString(toDate(toTimeZone(window_start, 'America/New_York'))), ' 16:00:00'), 'America/New_York')) AS cl
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker = 'SPY'
      AND window_start >= '2025-06-24 04:00:00'
      AND window_start < '2026-07-01 08:00:00'
    GROUP BY day
),
monthly AS (
    SELECT toStartOfMonth(day) AS month_start,
           argMax(cl, day) AS month_close
    FROM daily
    GROUP BY month_start
),
rets AS (
    SELECT month_start,
           formatDateTime(month_start, '%Y-%m') AS month,
           round(month_close, 2) AS month_final_close,
           100 * (month_close / lagInFrame(month_close) OVER (ORDER BY month_start) - 1) AS r
    FROM monthly
)
SELECT month,
       month_final_close,
       round(r, 2) AS simple_return_pct,
       round(100 * log(1 + r / 100), 2) AS log_return_pct
FROM rets
WHERE isFinite(r)
ORDER BY month_start

Twelve close-to-close months, from 2.28% inside 2025-07 go reach -1.33% inside 2026-06 — the last row na the same SPY June 2026 figure wey the first panel measure. Link the twelve properly and compare am with the shortcut of just adding the percentages:

QuerySum vs compound — twelve SPY monthly returns wey dem link three ways
The exact SQL behind every number
WITH daily AS (
    SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS day,
           argMaxIf(toFloat64(close), window_start,
               toTimeZone(window_start, 'America/New_York') >= toDateTime(concat(toString(toDate(toTimeZone(window_start, 'America/New_York'))), ' 09:30:00'), 'America/New_York')
               AND toTimeZone(window_start, 'America/New_York') < toDateTime(concat(toString(toDate(toTimeZone(window_start, 'America/New_York'))), ' 16:00:00'), 'America/New_York')) AS cl
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker = 'SPY'
      AND window_start >= '2025-06-24 04:00:00'
      AND window_start < '2026-07-01 08:00:00'
    GROUP BY day
),
monthly AS (
    SELECT toStartOfMonth(day) AS month_start,
           argMax(cl, day) AS month_close
    FROM daily
    GROUP BY month_start
),
rets AS (
    SELECT month_start,
           100 * (month_close / lagInFrame(month_close) OVER (ORDER BY month_start) - 1) AS r
    FROM monthly
),
ends AS (
    SELECT argMin(month_close, month_start) AS base_close,
           argMax(month_close, month_start) AS final_close
    FROM monthly
)
SELECT count() AS months_linked,
       round(sum(r), 2) AS simple_sum_pct,
       round(100 * (exp(sum(log(1 + r / 100))) - 1), 2) AS geometric_compound_pct,
       round(100 * (any(final_close) / any(base_close) - 1), 2) AS direct_twelve_month_pct,
       round(100 * (exp(sum(log(1 + r / 100))) - 1) - sum(r), 2) AS compounding_gap_points,
       round(sum(100 * log(1 + r / 100)), 2) AS sum_of_monthly_log_returns_pct,
       round(100 * log(any(final_close) / any(base_close)), 2) AS twelve_month_log_return_pct
FROM rets CROSS JOIN ends
WHERE isFinite(r)

The twelve months sum to 19.83% but dem compound to 20.79% — and the direct calculation, from June 2025 close straight to June 2026 close, give 20.79%, wey match the compounded figure instead of the sum. The gap here na 0.96 points for one calm-ish year; e dey wide more with volatility and horizon. Adding monthly percentages na common spreadsheet habit, and e dey quietly wrong.

The last two columns show the convention wey dem build for adding: the log return, the natural logarithm of the growth factor, ln(1 + return). Monthly log returns sum to 18.89% — identical to the twelve-month log return of 18.89%, and na exactly why analysts and risk models dey use dem. For small months the two conventions hardly differ (2025-11: 0.21% simple, 0.21% log); the bigger the month, the wider dem split (2026-04: 10.49% simple, 9.97% log). Fund fact sheets dey quote simple returns; log returns dey live inside research papers and risk models.

Di checklist wey fit settle any two sources

Wen two monthly return figures no gree, di wahala almost always dey inside one of dis four switches:

  1. Endpoints — close-to-close (di standard) or open-to-close (di month own trading). Dem go dey different any time wey di boundary gap.
  2. Dividends — price return or total return. Dem go dey different for any month wey get ex-dividend date.
  3. Adjustment — raw prints or di adjusted-close series. Dem go dey wildly different for any month wey get split.
  4. Di close itself — official auction print (di standard) against a last trade wey fit include extended hours.

Run di same switches for both sources and di disagreement go vanish — and any fund-versus-index comparison dey valid only wen both figures dey on di same four settings; if you mismatch one, di comparison go quietly measure di switch instead of di performance. Our own June 2026 recap dey state im convention up front for exactly dis reason — every monthly number for there na close-to-close, official prints, price return.

Monthly returns FAQ

Wetin be close-to-close return?

Na di percentage change from di final regular-session closing price of di period wey pass to di final close of dis period. E be di standard convention for daily, monthly, and yearly stock returns wey dem dey use for fact sheets and index reports.

Why two websites fit show different returns for di same stock and month?

Almost always na one of four conventions dey cause am: different endpoints (close-to-close versus open-to-close), whether dem count dividends or not, raw prices versus split-adjusted prices, or a different source for di closing price. NVDA June 2026 measures -5.39% or -7.4% based on di first switch alone.

Stock split dey change monthly return calculation?

E no suppose change am — split dey change di share count and price together, so position value no move — but e fit spoil any calculation wey dem do on raw prices. NVDA June 2024 look like -88.75% on raw closes and measure 12.45% once dem adjust for di 10-for-1 split.

Monthly stock returns dey include dividends?

Na only total returns dey include dem. Price returns — including di headline S&P 500 index level — no dey include dem. For June 2026, di dividend add 0.25% to SPY month, na im be di gap between im price and total return.

Dem dey calculate my personal portfolio monthly return di same way?

Not once cash enter or comot. Single-security returns like di ones for dis page na time-weighted; an account wey get deposits and withdrawals need money-weighted (internal-rate-of-return) calculation, or di cash flows go pretend to be performance. Most brokers dey report time-weighted figure exactly to remove your deposits from di number.


Every figure wey dey above na stored, versioned query on di real tape — expand any panel SQL, or measure any ticker month under any convention for di Strasmore terminal.

#returns#conventions#dividends#splits#education