วิธีคำนวณผลตอบแทนหุ้นรายเดือน
ทำความเข้าใจวิธีการคำนวณผลตอบแทนหุ้นรายเดือน ทั้งการใช้ราคาปิด การรวมเงินปันผล และการปรับราคาจากการแตกหุ้น เพื่อให้เข้าใจความแตกต่างของข้อมูลราคา
หากถามว่าหุ้นตัวหนึ่งมีผลตอบแทนอย่างไรในเดือนมิถุนายน คุณอาจได้รับตัวเลขที่แตกต่างกันจากแหล่งข้อมูลต่างๆ ซึ่งตัวเลขเหล่านั้นถูกต้องทั้งหมด ผลตอบแทนรายเดือนของหุ้นถูกกำหนดโดยปัจจัยที่คนส่วนใหญ่ไม่ทราบ ได้แก่ ราคาใดสองราคาที่ใช้กำหนดจุดเริ่มต้นและจุดสิ้นสุดของเดือน มีการรวมเงินปันผลหรือไม่ และ มีการปรับราคา (adjusted) เพื่อรองรับการแตกหุ้นหรือไม่ หน้าเว็บนี้จะนำวิธีการคำนวณที่แตกต่างกันมาเปรียบเทียบกันโดยใช้ข้อมูลราคาจริง เพื่อให้คุณเข้าใจถึงสาเหตุของความแตกต่างเหล่านั้นอย่างชัดเจน
Close-to-close: มาตรฐานที่ใช้กันทั่วไป
เกณฑ์ที่ผู้ให้บริการดัชนี, เอกสารข้อมูลกองทุน (fund fact sheets) และหน้าแสดงราคาโดยทั่วไปใช้ คือ ผลตอบแทนรายเดือนจะคำนวณตั้งแต่ ราคาปิดของเซสชันปกติสุดท้ายของเดือนก่อนหน้า ไปจนถึง ราคาปิดสุดท้ายของเดือนนี้ โดยราคาปิดสุดท้ายของเดือนพฤษภาคมจะเป็นจุดเริ่มต้นของเดือนมิถุนายน การคำนวณนั้นเรียบง่าย คือ นำราคาปิดลบด้วยราคาเริ่มต้น แล้วหารด้วยราคาเริ่มต้น ตัวอย่างเบื้องต้น: หุ้นที่ปิดเดือนพฤษภาคมที่ $50.00 และปิดเดือนมิถุนายนที่ $53.50 จะมีผลตอบแทนคือ (53.50 − 50.00) ÷ 50.00 = 0.07 หรือ 7% สำหรับเดือนนั้น ทุกเกณฑ์ที่ระบุในหน้านี้ใช้ข้อมูลนำเข้าที่แตกต่างกันเข้าสู่สูตรเดียวกัน ต่อไปนี้คือข้อมูลจริงของเดือนมิถุนายน 2026 เปรียบเทียบกับเกณฑ์ทางเลือกหลัก:
SQL เบื้องหลังตัวเลขแต่ละตัว
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 แบบ close-to-close คือ -5.39% แต่แบบ -7.4% วัดจากราคาเปิดของเซสชันปกติแรกของเดือนมิถุนายนไปจนถึงราคาปิดสุดท้าย หุ้นตัวเดิม เดือนเดิม แต่มีส่วนต่างระหว่างคำตอบที่ถูกต้องทั้งสองแบบอยู่ 2.01 percentage points ความแตกต่างทั้งหมดเกิดจากช่วงเวลาข้ามคืนและวันหยุดเพียงช่วงเดียว: NVDA ปิดเดือนพฤษภาคมที่ $211.15 และเปิดเดือนมิถุนายนที่ $215.73 — ส่วนต่างที่เพิ่มขึ้นนี้ถือเป็นส่วนหนึ่งของ "เดือนมิถุนายน" ในเกณฑ์ open-to-close และถือเป็นส่วนต่างที่รอยต่อในเกณฑ์ close-to-close สำหรับ SPY แสดงโครงสร้างแบบเดียวกันในระดับที่น้อยกว่า: -1.33% แบบ close-to-close เทียบกับ -1.2% แบบ open-to-close
ไม่มีตัวเลขใดผิด เกณฑ์ close-to-close ตอบคำถามว่า "ผู้ที่ถือหุ้นต่อเนื่องได้รับผลตอบแทนอย่างไรในช่วงรอยต่อของเดือน?" ส่วนเกณฑ์ open-to-close ตอบคำถามว่า "การซื้อขายภายในเดือนนั้นสร้างผลตอบแทนเท่าใดแก่ผู้ที่ซื้อ ณ ราคาเปิดของเดือนมิถุนายน?" ตัวเลขทั้งสองจะต่างกันเมื่อตลาดเกิดช่องว่างราคา (gap) ข้ามรอยต่อ ซึ่ง ช่องว่างราคาข้ามคืน เป็นเรื่องปกติที่เกิดขึ้นได้ทั่วไป ไม่ใช่เรื่องหายาก
เงินปันผล: ผลตอบแทนจากส่วนต่างราคา เทียบกับ ผลตอบแทนรวม
ทางเลือกที่สองคือ เงินสดที่หุ้นจ่ายออกมาในระหว่างเดือนนั้นถูกนำมาคำนวณด้วยหรือไม่? price return จะไม่นำมาคำนวณ แต่ total return จะนำเงินส่วนนั้นกลับมาบวกเพิ่ม สำหรับเดือนเดี่ยวๆ ส่วนใหญ่ ความแตกต่างนี้จะมีเพียงเล็กน้อย เนื่องจากผู้จ่ายปันผลรายไตรมาสจะจ่ายเงินประมาณหนึ่งในสี่ของอัตราผลตอบแทนต่อไตรมาส แต่ความแตกต่างนี้จะทวีคูณขึ้นจนกลายเป็นช่องว่างขนาดใหญ่เมื่อเวลาผ่านไปหลายปี และเดือน มิถุนายน 2026 เป็นเดือนที่มีการจ่ายปันผลของ SPY พอดี:
SQL เบื้องหลังตัวเลขแต่ละตัว
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 dvprice return ของ SPY ในเดือน มิถุนายน คือ -1.33% เมื่อบวกเงินปันผลจำนวน $1.9035 ต่อหุ้นที่ขึ้นเครื่องหมาย ex-dividend ในระหว่างเดือน จะทำให้ total return เพิ่มขึ้นเป็น -1.08% ซึ่งคิดเป็นสัดส่วนจากเงินสดที่ 0.25% ตระกูลดัชนีมีการเผยแพร่ข้อมูลทั้งสองรูปแบบ (ดัชนี "S&P 500" ที่คุณเห็นมักจะเป็น price index) หน้าแสดงผลการดำเนินงานของกองทุนถูกกำหนดให้ต้องแสดงค่า total return และเครื่องมือคัดกรองหุ้นมีการใช้ทั้งสองค่าผสมกันอย่างอิสระ เมื่อข้อมูลจากสองแหล่งไม่ตรงกันเพียงเล็กน้อยในเดือนที่มีการจ่ายปันผล เงินปันผลคือสาเหตุแรกที่ต้องสงสัย เนื่องจาก วันที่ขึ้นเครื่องหมาย ex-dividend เป็นตัวกำหนดว่าเงินปันผลนั้นจะถูกนับรวมในเดือนใด
ความแตกต่างในเดือน มิถุนายน เพียงเดือนเดียวมีค่าเพียงเศษส่วนของเปอร์เซ็นต์ แต่ระยะเวลาในการถือครองคือสิ่งที่ทำให้การคำนวณทั้งสองแบบแตกต่างกันอย่างชัดเจน ต่อไปนี้คือการคำนวณแบบเดียวกันที่ขยายระยะเวลาออกไปเป็นห้าปี:
SQL เบื้องหลังตัวเลขแต่ละตัว
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จากราคาปิดในเดือน มิถุนายน 2021 ที่ $428.07 ไปยังราคาปิดในเดือน มิถุนายน 2026 ที่ $746.32 price return ระยะเวลาห้าปีของ SPY วัดค่าได้ที่ 74.35% ในช่วงเวลาเดียวกันนี้มีการจ่ายเงินปันผลรวม 20 คิดเป็นเงิน $34.07 ต่อหุ้น เมื่อรวมเงินสดดังกล่าวจะทำให้ตัวเลขเพิ่มขึ้นเป็น 82.3% ซึ่งมีส่วนต่างอยู่ 7.96 จุด แม้จะยังไม่มีการนำเงินปันผลแม้แต่ครั้งเดียวไปลงทุนต่อก็ตาม ดัชนีแบบ total-return ที่แท้จริงจะนำเงินปันผลแต่ละครั้งไปลงทุนต่อทันทีในวันที่ขึ้นเครื่องหมาย ex-date และการนำหุ้นที่ได้จากการลงทุนต่อมาคำนวณจะยิ่งทำให้ช่องว่างนี้กว้างขึ้นไปอีก สำหรับหุ้นที่มีการจ่ายปันผลสม่ำเสมอ คำว่า "ส่วนต่างขนาดใหญ่" ไม่ใช่เพียงคำเปรียบเทียบ แต่เป็นผลลัพธ์ปกติที่เกิดจากการได้รับเงินสดรายไตรมาสต่อเนื่องกันหลายปี
ราคาปิดแบบปรับปรุง (Adjusted close) เทียบกับราคาปิดจริง (Raw close): กับดักจากการแตกหุ้น
เครื่องมือทำกราฟส่วนใหญ่ เช่น Yahoo Finance, Google Finance และแอปพลิเคชันของโบรกเกอร์ส่วนใหญ่ จะแสดงค่า adjusted close: ซึ่งเป็นชุดข้อมูลราคาในอดีตที่ผ่านการหารด้วยอัตราส่วนการแตกหุ้น (และในบางแพลตฟอร์มมีการปรับปรุงเรื่องเงินปันผลด้วย) เพื่อให้สามารถเปรียบเทียบราคาผ่านช่วงเวลาต่างๆ ได้ ส่วนคลังข้อมูลของเราจะเก็บข้อมูลราคาตามที่เกิดขึ้นจริงในตลาด ในเดือนปกติ ข้อมูลทั้งสองชุดนี้จะมีค่าตรงกัน แต่ในเดือนที่มีการแตกหุ้น ข้อมูลจะแสดงผลต่างกันอย่างมาก เช่น กรณีที่ NVDA แตกหุ้นในอัตราส่วน 10-for-1 เมื่อวันที่ 10 มิถุนายน 2024 ซึ่งเกิดขึ้นภายในเดือนดังกล่าว:
SQL เบื้องหลังตัวเลขแต่ละตัว
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หากดูจากราคาจริง NVDA "ลดลง" จาก $1097.46 เหลือ $123.41 ในเดือนมิถุนายนนั้น ซึ่งคิดเป็นการดิ่งลงถึง -88.75% แต่ในความเป็นจริงไม่ได้เกิดเหตุการณ์ดังกล่าวขึ้น ในวันที่เกิดการแตกหุ้น หุ้นเดิมหนึ่งหุ้นจะกลายเป็นหุ้นใหม่ 10 หุ้น และราคาจะถูกหารลงเพื่อให้สอดคล้องกัน ส่งผลให้มูลค่าเงินของผู้ถือหุ้นทุกคนยังคงเท่าเดิม หากนำราคาปิดเดือนพฤษภาคมมาหารด้วยอัตราส่วนเดียวกัน ซึ่งเท่ากับ $109.75 เมื่อปรับปรุงตามการแตกหุ้นแล้ว ราคาในเดือนมิถุนายน 2024 จะวัดค่าได้ที่ 12.45% ซึ่งเป็นเดือนที่ราคาปรับตัวขึ้น แหล่งข้อมูลใดก็ตามที่ใช้ข้อมูลแบบ adjusted จะรายงานค่าที่ใกล้เคียงกับตัวเลขที่สอง ส่วนการคำนวณแบบผิวเผินจากราคาจริงจะรายงานค่าแรก ราคาแบบ adjusted close คือสาเหตุที่พบบ่อยที่สุดที่ทำให้แหล่งข้อมูลสองแห่งรายงานผลตอบแทนรายเดือนในอดีตต่างกันอย่างสิ้นเชิง หากพบว่าตัวเลขมีค่าสูงหรือต่ำผิดปกติ ให้ตรวจสอบ ปฏิทินการแตกหุ้น เป็นอันดับแรก
ราคา "ปิด" ที่แท้จริงคือราคาใด?
ยังมีข้อจำกัดอีกประการหนึ่งที่ซ่อนอยู่ในธรรมเนียมปฏิบัติ นั่นคือราคาปิด ตัวเลขที่ใช้ในเอกสารข้อมูลสำคัญทุกฉบับคือราคาจากการประมูลปิดตลาดอย่างเป็นทางการ ซึ่งเป็นราคาเดียวที่กำหนดโดย การประมูลปิดตลาด เมื่อเวลา 16:00 น. ET อย่างไรก็ตาม การซื้อขายยังคงดำเนินต่อไปในช่วงหลังเวลาทำการ (after hours) และราคาล่าสุดของช่วงเวลาที่ขยายออกไปอาจแตกต่างจากราคาปิดอย่างเป็นทางการ:
SQL เบื้องหลังตัวเลขแต่ละตัว
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'
)เมื่อวันที่ 30 มิถุนายน ราคาทั้งสองต่างกันอยู่ -0.003% ซึ่งถือว่าน้อยมากในเย็นวันนั้น แต่ในคืนที่มีการประกาศผลประกอบการของหุ้นรายตัว ช่วงเวลาที่ขยายออกไปอาจทำให้ราคาเคลื่อนไหวในระดับเปอร์เซ็นต์ ซึ่งจะส่งผลให้ผลตอบแทนรายเดือนอย่างเป็นทางการถูกนำไปคำนวณในเดือน ถัดไป การซื้อขายช่วงหลังเวลาทำการและก่อนตลาดเปิด จะอธิบายรายละเอียดเกี่ยวกับข้อจำกัดดังกล่าว กรณีพิเศษที่เกี่ยวข้องอีกประการหนึ่งคือ การสิ้นสุดของเดือนอาจตรงกับวันหยุดซึ่งทำให้เวลาทำการสั้นลง โดยตลาดจะปิดทำการปกติในเวลา 13:00 น. ET และราคาปิดอย่างเป็นทางการจะถูกกำหนดในการประมูลปิดตลาดที่เร็วขึ้น ซึ่งสามารถตรวจสอบได้จาก ตารางวันหยุดตลาด
การคิดผลตอบแทนทบต้นสิบสองเดือนเป็นรายปี
ผลตอบแทนรายปีไม่ใช่ผลรวมของผลตอบแทนรายเดือนทั้งสิบสองเดือน แต่คือผลคูณของผลตอบแทนเหล่านั้น สูตรการคำนวณคือ: เปลี่ยนผลตอบแทนแต่ละเดือนให้เป็นปัจจัยการเติบโต (1 + ผลตอบแทน) จากนั้นนำปัจจัยทั้งสิบสองตัวมาคูณกัน แล้วลบออกด้วย 1 หากเดือนหนึ่งติดลบ 5% และเดือนถัดไปบวก 5% ผลรวมจะเท่ากับศูนย์ แต่หากคิดแบบทบต้นจะได้ 0.95 × 1.05 − 1 = −0.25% ต่อไปนี้คือเส้นทางรายเดือนที่เกิดขึ้นจริงของ SPY ในช่วงสิบสองเดือนที่สิ้นสุดใน มิถุนายน 2026:
SQL เบื้องหลังตัวเลขแต่ละตัว
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ข้อมูลรายเดือนสิบสองเดือน ตั้งแต่ 2.28% ใน 2025-07 ถึง -1.33% ใน 2026-06 โดยแถวสุดท้ายคือตัวเลข SPY ประจำเดือน มิถุนายน 2026 ชุดเดียวกับที่แผงแรกวัดค่าไว้ เมื่อคำนวณแบบทบต้นอย่างถูกต้องและเปรียบเทียบกับการใช้วิธีลัดด้วยการบวกเปอร์เซ็นต์เข้าด้วยกัน:
SQL เบื้องหลังตัวเลขแต่ละตัว
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)ผลรวมสิบสองเดือนคือ 19.83% แต่เมื่อคิดแบบทบต้นจะได้ 20.79% และการคำนวณโดยตรงจากราคาปิดเดือน มิถุนายน 2025 ไปยังเดือน มิถุนายน 2026 จะได้ 20.79% ซึ่งตรงกับตัวเลขแบบทบต้นมากกว่าการใช้ผลรวม ส่วนต่างในที่นี้คือ 0.96 จุด ในปีที่มีความผันผวนต่ำ; ส่วนต่างนี้จะกว้างขึ้นตามความผันผวนและระยะเวลา การบวกเปอร์เซ็นต์รายเดือนเป็นนิสัยที่พบได้บ่อยในสเปรดชีต ซึ่งเป็นวิธีที่ผิด
สองคอลัมน์สุดท้ายแสดงวิธีการคำนวณที่เป็นมาตรฐานสำหรับการบวก นั่นคือ log return ซึ่งคือค่าลอการิทึมธรรมชาติของปัจจัยการเติบโต หรือ ln(1 + return) ผลรวมของ log return รายเดือนคือ 18.89% ซึ่งเท่ากับ log return สิบสองเดือนที่ 18.89% นี่คือเหตุผลที่นักวิเคราะห์และแบบจำลองความเสี่ยงนิยมใช้ค่านี้ สำหรับเดือนที่มีการเปลี่ยนแปลงน้อย วิธีการทั้งสองแทบไม่ต่างกัน (2025-11: 0.21% แบบธรรมดา, 0.21% แบบ log) แต่ยิ่งเดือนมีการเปลี่ยนแปลงมาก ส่วนต่างจะยิ่งกว้างขึ้น (2026-04: 10.49% แบบธรรมดา, 9.97% แบบ log) ทั้งนี้ ข้อมูลสรุปกองทุน (fund fact sheets) มักระบุผลตอบแทนแบบธรรมดา ส่วน log return มักใช้ในงานวิจัยและคณิตศาสตร์ด้านความผันผวน
รายการตรวจสอบเพื่อปรับข้อมูลจากสองแหล่งให้ตรงกัน
เมื่อตัวเลขผลตอบแทนรายเดือนจากสองแหล่งไม่ตรงกัน มักเกิดจากความแตกต่างในหนึ่งในสี่ปัจจัยหลัก ดังนี้:
- Endpoints — แบบ close-to-close (มาตรฐานทั่วไป) หรือแบบ open-to-close (การซื้อขายภายในเดือนนั้น) ข้อมูลจะต่างกันทันทีหากมีช่องว่างราคา (gap) ที่จุดเริ่มต้นหรือจุดสิ้นสุด
- Dividends — แบบ price return หรือแบบ total return ข้อมูลจะต่างกันในเดือนที่มีการขึ้นเครื่องหมาย ex-dividend
- Adjustment — แบบราคาดิบ (raw prints) หรือแบบราคาปรับปรุง (adjusted-close series) ข้อมูลจะต่างกันอย่างมากในเดือนที่มีการแตกหุ้น (split)
- The close itself — ราคาปิดจากการประมูลอย่างเป็นทางการ (มาตรฐาน) เทียบกับราคาจากการซื้อขายครั้งสุดท้ายซึ่งอาจรวมช่วงเวลาการซื้อขายนอกเวลาทำการ (extended hours)
หากใช้ปัจจัยเดียวกันกับข้อมูลทั้งสองแหล่ง ความคลาดเคลื่อนจะหมดไป การเปรียบเทียบระหว่างกองทุนกับดัชนีจะมีความสมบูรณ์ก็ต่อเมื่อตัวเลขทั้งสองใช้การตั้งค่าทั้งสี่แบบที่เหมือนกัน หากตั้งค่าไม่ตรงกันเพียงข้อเดียว การเปรียบเทียบนั้นจะกลายเป็นการวัดความแตกต่างของปัจจัยที่เลือกใช้แทนที่จะเป็นการวัดผลการดำเนินงาน สรุปผลเดือนมิถุนายน 2026 ของเราได้ระบุข้อกำหนดไว้ล่วงหน้าด้วยเหตุผลนี้ โดยตัวเลขรายเดือนทุกตัวในนั้นใช้แบบ close-to-close, ราคาปิดอย่างเป็นทางการ และแบบ price return
คำถามที่พบบ่อยเกี่ยวกับผลตอบแทนรายเดือน
Close-to-close return คืออะไร?
คือการเปลี่ยนแปลงเป็นเปอร์เซ็นต์จากราคาปิดในเซสชันปกติครั้งสุดท้ายของงวดก่อนหน้า มายังราคาปิดครั้งสุดท้ายของงวดปัจจุบัน นี่คือมาตรฐานสากลสำหรับการคำนวณผลตอบแทนหุ้นรายวัน รายเดือน และรายปี ในเอกสารข้อมูลสรุป (fact sheets) และรายงานดัชนี
ทำไมเว็บไซต์สองแห่งจึงแสดงผลตอบแทนของหุ้นและเดือนเดียวกันไม่เท่ากัน?
มักเกิดจากหนึ่งในสี่กรณีต่อไปนี้: การใช้จุดสิ้นสุดที่ต่างกัน (close-to-close เทียบกับ open-to-close), การนับรวมเงินปันผลหรือไม่นับ, การใช้ราคาดิบเทียบกับราคาที่ปรับปรุงตามการแตกหุ้น (split-adjusted), หรือการใช้แหล่งข้อมูลราคาปิดที่ต่างกัน ข้อมูลของ NVDA ในเดือนมิถุนายน 2026 มีค่าต่างกันระหว่าง -5.39% หรือ -7.4% จากการเปลี่ยนเกณฑ์เพียงอย่างเดียว
การแตกหุ้น (stock split) ทำให้การคำนวณผลตอบแทนรายเดือนเปลี่ยนไปหรือไม่?
ไม่ควรเปลี่ยน เนื่องจากการแตกหุ้นจะเปลี่ยนทั้งจำนวนหุ้นและราคาไปพร้อมกัน ทำให้มูลค่าของสถานะคงเดิม แต่การแตกหุ้นจะทำให้การคำนวณที่ใช้ราคาดิบผิดพลาด ข้อมูลของ NVDA ในเดือนมิถุนายน 2024 มีค่าเป็น -88.75% เมื่อใช้ราคาปิดดิบ และมีค่าเป็น 12.45% เมื่อปรับปรุงตามการแตกหุ้นในอัตรา 10-for-1 แล้ว
ผลตอบแทนหุ้นรายเดือนรวมเงินปันผลหรือไม่?
เฉพาะผลตอบแทนรวม (total returns) เท่านั้นที่รวม ส่วนผลตอบแทนด้านราคา (price returns) ซึ่งรวมถึงระดับดัชนี S&P 500 หลัก จะไม่รวมเงินปันผล ในเดือนมิถุนายน 2026 เงินปันผลได้เพิ่มค่า 0.25% ให้กับผลตอบแทนรายเดือนของ SPY ซึ่งเป็นส่วนต่างระหว่างราคาและผลตอบแทนรวม
ผลตอบแทนรายเดือนของพอร์ตโฟลิโอส่วนตัวฉันคำนวณด้วยวิธีเดียวกันหรือไม่?
ไม่ หากมีการเคลื่อนย้ายเงินสดเข้าหรือออกจากพอร์ต ผลตอบแทนของหลักทรัพย์เดี่ยวอย่างที่ปรากฏในหน้านี้ใช้การคำนวณแบบถ่วงน้ำหนักด้วยเวลา (time-weighted) ส่วนบัญชีที่มีการฝากและถอนเงินจำเป็นต้องใช้การคำนวณแบบถ่วงน้ำหนักด้วยเงิน (money-weighted หรือ internal-rate-of-return) มิฉะนั้นกระแสเงินสดจะทำให้ค่าผลตอบแทนคลาดเคลื่อน โบรกเกอร์ส่วนใหญ่จึงรายงานตัวเลขแบบถ่วงน้ำหนักด้วยเวลาเพื่อตัดรายการฝากเงินของคุณออกจากตัวเลขดังกล่าว
ตัวเลขทั้งหมดข้างต้นมาจากการดึงข้อมูล (query) ที่จัดเก็บและระบุเวอร์ชันจากข้อมูลตลาดจริง — คุณสามารถขยายคำสั่ง SQL ของแต่ละแผงข้อมูล หรือวัดผลตอบแทนรายเดือนของหุ้นใดก็ได้ภายใต้เกณฑ์ใดก็ได้บนเทอร์มินัล Strasmore