top of page
Portfolio Management Toolkit Notesสารบัญ
Portfolio Management Toolkit Notesพิมพ์หน้านี้

Advanced · Style & Factors · บทย่อย 1

Factor investing จากผลตอบแทนสู่ beta และ alpha

กองทุนขึ้นมากกว่าตลาด เพราะผู้จัดการเลือกหุ้นเก่ง หรือเพราะกองทุนตอบสนองต่อการขึ้นลงของตลาดแรงกว่า?

สมมติว่าตลาดขึ้น 2% และกองทุนขึ้น 3% ในเดือนเดียวกัน เรารู้ว่ากองทุนได้ผลตอบแทนมากกว่า แต่ตัวเลขสองตัวนี้ยังแยกเหตุผลไม่ได้ กองทุนอาจถือหุ้นที่เคลื่อนไหวแรงตามตลาด อาจได้รับผลจากปัจจัยอื่น หรือมีข่าวเฉพาะหุ้นในเดือนนั้น เราจะเริ่มจากแบบจำลองที่ใช้การเคลื่อนไหวของตลาดเพียงปัจจัยเดียว แล้วคำนวณว่าส่วนใดสัมพันธ์กับตลาดและส่วนใดยังอธิบายไม่ได้

บทนี้ประกอบหัวข้อ Style & Factors ของคอร์ส Advanced Portfolio Construction and Analysis with Python โดยเรียบเรียงคำอธิบายและตัวอย่างใหม่จากบท Introduction to factor investing และ Factor models and the CAPM ตัวเลขทุกชุดเป็นข้อมูลสมมติ ไม่มีผลตอบแทนกองทุนหรือตลาดจริง ใช้ความรู้เรื่องผลตอบแทนและCovarianceจากบทก่อนหน้าได้ ส่วนคำสั่ง Python ที่ใช้เพิ่มจะอธิบายก่อนคำนวณ

เปิด Notebook ใหม่แล้วรันตัวอย่างตามลำดับ โค้ดใช้ NumPy, pandas และ SciPy โดยไม่ต้องมีไฟล์ข้อมูลหรือ toolkit ของคอร์ส ดาวน์โหลด Notebook ของบทนี้

ปัจจัยร่วมทำให้กองทุนต่างชื่อเคลื่อนไหวคล้ายกัน

กองทุน A และ B อาจถือหุ้นคนละบริษัท แต่ทั้งสองได้รับผลกระทบจากภาวะเศรษฐกิจ ดอกเบี้ย และความต้องการรับความเสี่ยงของนักลงทุนเหมือนกัน การนับจำนวนชื่อกองทุนจึงบอกได้เพียงว่าถือกี่ผลิตภัณฑ์ เราต้องดูด้วยว่าผลตอบแทนแต่ละกองทุนสัมพันธ์กับสิ่งใด

ในบริบทนี้ factor คือปัจจัยร่วมที่เราใช้ศึกษาและอธิบายการเคลื่อนไหวของผลตอบแทน เช่น ผลตอบแทนตลาด การเปลี่ยนแปลงของอัตราดอกเบี้ย หรือผลตอบแทนของกลุ่มหุ้นขนาดเล็กเทียบกับขนาดใหญ่ ปัจจัยมีหน่วยต่างกันได้ ดังนั้นก่อนคูณตัวเลขต้องระบุว่าปัจจัยนั้นวัดอะไร

คำสามคำที่มักอยู่ใกล้กันมีหน้าที่ต่างกัน:

คำ ตัวอย่าง สิ่งที่ตัวเลขบอก
ลักษณะของบริษัท หรือ characteristic มูลค่าตลาดของบริษัท 20,000 ล้านบาท ขนาดบริษัท ณ วันที่วัด
ผลตอบแทนปัจจัย หรือ factor return เดือนนี้พอร์ตหุ้นเล็กให้ผลตอบแทนสูงกว่าพอร์ตหุ้นใหญ่ 0.8 จุดเปอร์เซ็นต์ สิ่งที่เกิดขึ้นกับปัจจัยในเดือนนั้น
ความไวต่อปัจจัย หรือ loading กองทุนมี loading ต่อปัจจัยดังกล่าว 0.6 ผลตอบแทนกองทุนสัมพันธ์กับการเปลี่ยนแปลงของปัจจัยเท่าไรในแบบจำลอง

บริษัทเล็กจึงไม่ได้มี loading เท่ากับมูลค่าตลาดของตัวเอง และ loading ของกองทุนก็ไม่ได้บอกสัดส่วนเงินที่ใส่ในหุ้นเล็กโดยตรง เราจะเห็นตัวเลขของความแตกต่างนี้ในบทหลายปัจจัย

การลงทุนตามปัจจัยมักกำหนดกฎคัดเลือกและถ่วงน้ำหนักสินทรัพย์ไว้ล่วงหน้า เช่น เรียงหุ้นตามลักษณะที่กำหนดแล้วปรับพอร์ตตามรอบ ผู้พัฒนากลยุทธ์ยังต้องเลือกข้อมูล จักรวาลหุ้น วิธีถ่วงน้ำหนัก และต้นทุนที่ยอมรับได้ กฎทำให้กระบวนการตรวจสอบและทำซ้ำได้ แต่ไม่ได้รับประกันผลตอบแทนสูงกว่าตลาด

ส่วน factor premium หมายถึงผลตอบแทนคาดหวังที่สัมพันธ์กับการรับปัจจัยนั้น ต้องแยกจากผลตอบแทนที่เกิดขึ้นจริงหนึ่งเดือน ปัจจัยที่ช่วยอธิบายความเสี่ยงอาจมี premium เป็นศูนย์ และค่าประมาณ premium จากข้อมูลก็อาจติดลบหรือเปลี่ยนตามช่วงเวลาได้

หักผลตอบแทนปลอดความเสี่ยงให้ตรงช่วงเวลา

สมมติว่าเดือนหนึ่งสินทรัพย์อ้างอิงที่ถือว่าปลอดความเสี่ยงในแบบจำลองให้ผลตอบแทน 0.2% ตลาดให้ 2% และกองทุนให้ 3% เราเรียกผลตอบแทนเกิน 0.2% นี้ว่า excess return หรือผลตอบแทนส่วนเกิน

ให้ Ri,tR_{i,t} เป็นผลตอบแทนกองทุน ii ในเดือน tt และ Rf,tR_{f,t} เป็นผลตอบแทนปลอดความเสี่ยงของเดือนเดียวกัน ตัวอักษรเล็ก rr ในบทนี้ใช้แทนผลตอบแทนส่วนเกิน:

ri,t=Ri,t−Rf,t,rm,t=Rm,t−Rf,t.r_{i,t}=R_{i,t}-R_{f,t},\qquad r_{m,t}=R_{m,t}-R_{f,t}.

ตัวห้อย mm หมายถึง market ทั้งสองฝั่งต้องใช้ช่วงเวลา สกุลเงิน และนิยามผลตอบแทนที่เข้ากัน เช่น ผลตอบแทนรวมปันผลของกองทุนควรเทียบกับผลตอบแทนรวมของดัชนี การนำอัตราดอกเบี้ยต่อปีมาหักผลตอบแทนกองทุนรายเดือนตรง ๆ จะผิดหน่วย

import numpy as np
import pandas as pd
from scipy import stats

fund_return = 0.03
market_return = 0.02
rf_month = 0.002
print(f"Fund excess: {fund_return - rf_month:.2%}")
print(f"Market excess: {market_return - rf_month:.2%}")

ได้ผลตอบแทนส่วนเกินกองทุน 2.80% และตลาด 1.80% 0.002 คือ 0.2% ส่วน :.2% เปลี่ยนเฉพาะรูปแบบแสดงผลให้เป็นเปอร์เซ็นต์สองตำแหน่ง ขณะคำนวณยังใช้ทศนิยม ผลต่าง 2.80% กับ 1.80% เท่ากับ 1 จุดเปอร์เซ็นต์ ไม่ใช่ผลตอบแทนที่ได้จากการหารสองจำนวนนี้

ผลตอบแทนปลอดความเสี่ยงเป็นข้อสมมติของแบบจำลองสำหรับช่วงเวลาและสกุลเงินที่กำหนด พันธบัตรระยะยาวที่ขายก่อนครบกำหนดอาจมีความเสี่ยงจากราคาตลาด จึงไม่ควรเลือกสินทรัพย์ใดก็ได้มาใช้แทน RfR_f เพียงเพราะชื่อมีคำว่าพันธบัตร

สร้างข้อมูล 12 เดือนที่ตรวจคำตอบได้

เราจะสร้างกองทุนสมมติให้มีความสัมพันธ์กับตลาดที่ทราบล่วงหน้า เพื่อดูว่า OLS กู้ความสัมพันธ์กลับมาได้อย่างไร ชุดนี้ตั้งใจเรียบง่าย ไม่ได้จำลองลักษณะทั้งหมดของตลาดจริง

ผลตอบแทนส่วนเกินตลาดเริ่มที่ −5% และเพิ่มทีละ 1 จุดเปอร์เซ็นต์จนถึง 6% เรากำหนดผลตอบแทนส่วนเกินกองทุนเป็น 0.1%+1.2×0.1\%+1.2\times ผลตอบแทนส่วนเกินตลาด แล้วเพิ่มส่วนที่แบบจำลองตลาดอธิบายไม่ได้ครั้งละ +0.4 หรือ −0.4 จุดเปอร์เซ็นต์

np.arange(12) สร้างเลข 0 ถึง 11 เมื่อคูณ 0.01 และลบ 0.05 จึงได้ตลาดตามที่กำหนด ส่วน np.tile([...], 3) ทำรายการในวงเล็บซ้ำสามรอบ pd.period_range สร้างป้ายเดือน 12 เดือน ความถี่ "M" หมายถึงเดือน โดยไม่ได้อ้างว่ามีการซื้อขายในวันใดวันหนึ่ง

months = pd.period_range("2025-01", periods=12, freq="M")
market_excess = np.arange(12) * 0.01 - 0.05
unexplained = np.tile([1, -1, -1, 1], 3) * 0.004
fund_excess = 0.001 + 1.2 * market_excess + unexplained
returns = pd.DataFrame({
    "Market": rf_month + market_excess,
    "Fund": rf_month + fund_excess,
    "RF": rf_month,
}, index=months)
print((returns.head(4) * 100).round(2))

สี่แถวแรกของ Market คือ −4.80%, −3.80%, −2.80%, −1.80% และ Fund คือ −5.30%, −4.90%, −3.70%, −1.70% คอลัมน์ RF เป็น 0.20% ทุกเดือน ตารางพิมพ์เป็นตัวเลขเปอร์เซ็นต์หลังคูณ 100 แต่ returns ที่เก็บไว้ยังเป็นทศนิยม .head(4) เลือกสี่แถวแรก และ .round(2) ปัดเฉพาะตารางที่แสดง

เมื่อมีข้อมูลจริง เราจะไม่รู้สมการที่สร้างผลตอบแทน และส่วนที่อธิบายไม่ได้ก็ไม่เดินเป็นรูปแบบซ้ำแบบนี้ ตัวอย่างจงใจเลือกให้คำนวณได้แน่นอนก่อนเพิ่มประเด็นการประมาณจากข้อมูลจริง

OLS หาเส้นตรงที่ใกล้ข้อมูลทั้งชุด

เขียนแบบจำลองกองทุนด้วยตลาดปัจจัยเดียวเป็น

ri,t=αi+βirm,t+εi,t.r_{i,t}=\alpha_i+\beta_i r_{m,t}+\varepsilon_{i,t}.

α\alpha อ่านว่า alpha เป็นค่าคงที่ของเส้นตรง มีหน่วยผลตอบแทนต่อเดือน β\beta อ่านว่า beta บอกว่าเมื่อผลตอบแทนส่วนเกินตลาดต่างกัน 1 จุดเปอร์เซ็นต์ ค่าที่เส้นตรงอธิบายให้กองทุนต่างกันกี่จุดเปอร์เซ็นต์ ส่วน εt\varepsilon_t เป็นส่วนที่เส้นตรงยังอธิบายไม่ได้ของแต่ละเดือน

เราใช้หมวกด้านบน α^,β^\hat\alpha,\hat\beta เพื่อบอกว่าเป็นค่าประมาณจากชุดข้อมูล วิธี Ordinary Least Squares หรือ OLS เลือกสองค่านี้ให้ผลรวมกำลังสองของความคลาดเคลื่อนต่ำที่สุด:

min⁡a,b∑t=1n(yt−a−bxt)2,xt=rm,t,yt=ri,t.\min_{a,b}\sum_{t=1}^{n}(y_t-a-bx_t)^2, \qquad x_t=r_{m,t},\quad y_t=r_{i,t}.

ยกกำลังสองเพื่อให้ความคลาดเคลื่อนบวกและลบไม่หักล้างกัน เส้นตรงที่อธิบายสูงเกินไป 2 จุดเปอร์เซ็นต์ในเดือนหนึ่งและต่ำเกินไป 2 จุดเปอร์เซ็นต์ในอีกเดือนยังมีความคลาดเคลื่อนทั้งคู่

สำหรับเส้นตรงที่มีทั้งจุดตัดแกนและความชัน สูตรคำตอบคือ

β^=∑t(xt−xˉ)(yt−yˉ)∑t(xt−xˉ)2,α^=yˉ−β^xˉ.\hat\beta=\frac{\sum_t(x_t-\bar x)(y_t-\bar y)} {\sum_t(x_t-\bar x)^2},\qquad \hat\alpha=\bar y-\hat\beta\bar x.

เครื่องหมายขีดบนหมายถึงค่าเฉลี่ย ตัวเศษดูว่าทั้งสองชุดเบี่ยงจากค่าเฉลี่ยไปด้วยกันเท่าไร ตัวส่วนวัดการกระจายของตลาด ถ้าตลาดเท่ากันทุกเดือน ตัวส่วนจะเป็นศูนย์และข้อมูลชุดนั้นไม่สามารถระบุความชันได้

x = (returns["Market"] - returns["RF"]).to_numpy()
y = (returns["Fund"] - returns["RF"]).to_numpy()
x_centered = x - x.mean()
y_centered = y - y.mean()
beta = np.sum(x_centered * y_centered) / np.sum(x_centered ** 2)
alpha = y.mean() - beta * x.mean()
print(f"Mean x: {x.mean():.3%}; mean y: {y.mean():.3%}")
print(f"Beta: {beta:.4f}; monthly alpha: {alpha:.3%}")

.to_numpy() เปลี่ยน Series เป็น array ของตัวเลข .mean() หาค่าเฉลี่ย np.sum รวมค่าทั้งชุด และ ** 2 คือยกกำลังสอง ผลลัพธ์คือค่าเฉลี่ยตลาดส่วนเกิน 0.500% กองทุนส่วนเกิน 0.700%, beta 1.2000 และ alpha 0.100% ต่อเดือน ตรงกับสมการที่ใช้สร้างข้อมูล

ตัวอย่างนี้แปลว่าเส้นตรงของกองทุนเปลี่ยน 1.2 จุดเปอร์เซ็นต์เมื่อผลตอบแทนส่วนเกินตลาดเปลี่ยน 1 จุดเปอร์เซ็นต์ Beta ไม่มีหน่วยเมื่อทั้งสองฝั่งเป็นผลตอบแทนหน่วยเดียวกัน ค่า 1.2 ไม่ได้แปลว่าต้องถือหุ้นตลาด 120% จริง และไม่ได้บอกว่ากองทุนขึ้นมากกว่าตลาดทุกเดือน

ผลตอบแทนส่วนเกินกองทุนสมมติ 12 จุดรอบเส้นตรง alpha 0.1% ต่อเดือนและ beta 1.2
ทั้งสองแกนเป็นผลตอบแทนส่วนเกินรายเดือนในหน่วยเปอร์เซ็นต์ เส้นประมาณจากข้อมูลสมมติ 12 เดือนนี้ ระยะในแนวตั้งระหว่างแต่ละจุดกับเส้นคือ residual

สูตร beta เชื่อมกับ covariance อย่างไร

ถ้าแบ่งทั้งเศษและส่วนในสูตร beta ด้วย n−1n-1 จะได้ sample covariance หารด้วย sample variance:

β^=Cov⁡^(y,x)Var⁡^(x)=ρ^xysysx.\hat\beta=\frac{\widehat{\operatorname{Cov}}(y,x)} {\widehat{\operatorname{Var}}(x)} =\hat\rho_{xy}\frac{s_y}{s_x}.

ρ\rho คือ correlation ส่วน sx,sys_x,s_y คือ sample standard deviation Beta จึงรวมทั้งความสัมพันธ์และขนาดการแกว่งไว้ด้วย Correlation อยู่ระหว่าง −1 กับ 1 แต่ beta อาจสูงกว่า 1 หรือติดลบได้

xy = pd.DataFrame({"Market excess": x, "Fund excess": y})
covariance = xy.cov()
beta_cov = covariance.loc["Fund excess", "Market excess"] / xy["Market excess"].var()
rho = xy["Market excess"].corr(xy["Fund excess"])
beta_rho = rho * xy["Fund excess"].std() / xy["Market excess"].std()
print(f"Covariance beta: {beta_cov:.4f}")
print(f"Correlation: {rho:.6f}; correlation-based beta: {beta_rho:.4f}")

ได้ beta 1.2000 ทั้งสองวิธี และ correlation ประมาณ 0.995370 เมธอด .cov(), .var() และ .std() ของ pandas ใช้ตัวหาร n−1n-1 โดยปริยายในตัวอย่างนี้ .loc[ชื่อแถว, ชื่อคอลัมน์] เลือกช่องจากตาราง covariance หากใช้ NumPy ต้องระบุ ddof=1 เมื่อต้องการ sample variance แบบเดียวกัน

แยกผลตอบแทนที่อธิบายได้กับ residual ทีละเดือน

ค่าบนเส้นตรงเรียกว่า fitted value: y^t=α^+β^xt\hat y_t=\hat\alpha+\hat\beta x_t ส่วน residual คือ ε^t=yt−y^t\hat\varepsilon_t=y_t-\hat y_t เราใช้คำว่า residual กับค่าที่คำนวณหลัง fit และใช้ error term กับความคลาดเคลื่อนในแบบจำลองที่ไม่ทราบค่าจริง

fitted = alpha + beta * x
residual = y - fitted
attribution = pd.DataFrame({
    "Actual excess": y,
    "Alpha": alpha,
    "Market contribution": beta * x,
    "Residual": residual,
}, index=months)
print((attribution.head(4) * 100).round(2))

เดือนแรกกองทุนมีผลตอบแทนส่วนเกิน −5.50% แบ่งเป็น alpha +0.10 จุดเปอร์เซ็นต์ ส่วนจากตลาด 1.2×(−5%)=−6.001.2\times(-5\%)=-6.00 จุดเปอร์เซ็นต์ และ residual +0.40 จุดเปอร์เซ็นต์ รวม 0.10−6.00+0.40=−5.500.10-6.00+0.40=-5.50 จุดเปอร์เซ็นต์ เมื่อต้องการกลับไปเป็นผลตอบแทนรวมจึงบวก RF 0.20% ได้ −5.30%

เดือนที่ตลาดส่วนเกินเป็นศูนย์ กองทุนในข้อมูลนี้ยังมีผลตอบแทนส่วนเกิน −0.30% เพราะ 0.10%−0.40%=−0.30%0.10\%-0.40\%=-0.30\% ค่า alpha เป็นระดับของเส้นตรงที่ x=0x=0 ผลที่เกิดขึ้นจริงยังมี residual กำกับอยู่ด้วย

Residual อาจเกิดจากข่าวบริษัท ปัจจัยที่ยังไม่ได้ใส่ไว้ ความสัมพันธ์ที่ไม่เป็นเส้นตรง หรือการเปลี่ยนนโยบายกองทุน การตั้งชื่อว่า error ไม่ได้พิสูจน์ว่ามันสุ่มอิสระหรือไม่มีข้อมูลให้ศึกษา

R² วัดความพอดีกับข้อมูลที่ใช้ fit

ผลรวมกำลังสองของ residual เรียก SSE ส่วนผลรวมกำลังสองของ yy ที่เบี่ยงจากค่าเฉลี่ยเรียก TSS สำหรับ OLS ที่มี intercept เราคำนวณ

R2=1−SSETSS=1−∑tε^t2∑t(yt−yˉ)2.R^2=1-\frac{\mathrm{SSE}}{\mathrm{TSS}} =1-\frac{\sum_t\hat\varepsilon_t^2}{\sum_t(y_t-\bar y)^2}.

ตัวหารคือความแปรผันของผลตอบแทนรอบค่าเฉลี่ย ไม่ใช่กำไรสะสมของกองทุน ค่านี้จึงบอกสัดส่วนความแปรผันในตัวอย่างที่เส้นตรงอธิบายได้

sse = np.sum(residual ** 2)
tss = np.sum(y_centered ** 2)
r_squared = 1 - sse / tss
print(f"SSE: {sse:.6f}; TSS: {tss:.6f}")
print(f"R-squared: {r_squared:.6f}")
print(f"Residual mean: {residual.mean():.10f}")
print(f"Centered x dot residual: {np.sum(x_centered * residual):.10f}")

SSE เท่ากับ 0.000192, TSS เท่ากับ 0.020784 และ R2≈0.990762R^2\approx0.990762 หรืออธิบายความแปรผันของชุดนี้ได้ประมาณ 99.08% ไม่ใช่ผลตอบแทน 99.08% และไม่ใช่โอกาสพยากรณ์ถูก 99.08% เราสร้างข้อมูลให้เกาะเส้นตรงมาก ค่า R2R^2 สูงนี้จึงเป็นคุณสมบัติของโจทย์สมมติ

สองบรรทัดสุดท้ายใกล้ศูนย์ เพราะ OLS ที่มี intercept ทำให้ผลรวม residual และผลคูณ residual กับตัวแปรอธิบายเป็นศูนย์ในข้อมูลที่ใช้ fit ตามเงื่อนไขของคำตอบ ความต่างระดับ 10−1610^{-16} อาจปรากฏจากการคำนวณเลขทศนิยม คุณสมบัตินี้ไม่ใช่หลักฐานว่าความคลาดเคลื่อนในอนาคตจะอิสระจากตลาด

ตรวจผลกับ SciPy และอ่านความไม่แน่นอนของ alpha

stats.linregress(x, y) fit เส้นตรงพร้อม intercept ให้ผลลัพธ์เป็นวัตถุที่มี .slope และ .intercept เราใช้มันเทียบกับสูตรที่คำนวณเองก่อนใช้เครื่องมือกับข้อมูลที่ใหญ่ขึ้น

fit = stats.linregress(x, y)
print(f"Slope: {fit.slope:.4f}; intercept: {fit.intercept:.3%}")
print(f"R-squared from SciPy: {fit.rvalue ** 2:.6f}")
print(f"Slope standard error: {fit.stderr:.6f}")
print(f"Intercept standard error: {fit.intercept_stderr:.3%}")

ได้ beta 1.2000, alpha 0.100%, R2=0.990762R^2=0.990762, standard error ของ beta ประมาณ 0.036642 และของ alpha ประมาณ 0.128% ต่อเดือน Standard error ประเมินการแกว่งของตัวประมาณภายใต้ข้อสมมติทางสถิติของวิธี ไม่ใช่ความผันผวนรายเดือนของกองทุน

ในข้อมูลจริง เราอาจตั้งคำถามว่าค่า alpha ต่างจากศูนย์มากเพียงใดเมื่อเทียบกับความไม่แน่นอนของการประมาณ ตัวอย่างต่อไปคำนวณช่วงแบบ Student's t สำหรับ intercept โดยมี degrees of freedom หรือองศาอิสระ n−2n-2 เพราะใช้ข้อมูลประมาณสองพารามิเตอร์

n = len(x)
degrees_freedom = n - 2
critical = stats.t.ppf(0.975, df=degrees_freedom)
alpha_interval = alpha + np.array([-1, 1]) * critical * fit.intercept_stderr
print(f"Degrees of freedom: {degrees_freedom}")
print(f"Illustrative 95% interval: {alpha_interval[0]:.3%} to {alpha_interval[1]:.3%}")

len(x) นับจำนวนเดือน และ .ppf(0.975, ...) หาจุดแบ่งที่มีพื้นที่ด้านซ้าย 97.5% ของการแจกแจง t เพื่อเหลือหางด้านละ 2.5% ได้ช่วงประมาณ −0.185% ถึง +0.385% ต่อเดือน ซึ่งครอบคลุมศูนย์

ช่วงนี้เป็นตัวอย่างการใช้สูตรเท่านั้น ข้อมูลของเราถูกสร้างเป็นรูปแบบตายตัว ไม่ใช่ตัวอย่างสุ่มที่ตรวจแล้วว่าเข้าเงื่อนไข ช่วง t แบบมาตรฐานจะตีความเป็นช่วงความเชื่อมั่นตามทฤษฎีได้ภายใต้แบบจำลองเส้นตรงที่ถูกต้อง และ error ที่เป็นอิสระ มีความแปรปรวนเท่ากัน และเป็น Normal สำหรับผลแบบ exact ในตัวอย่างเล็ก หากข้อมูลจริงมีความแปรปรวนเปลี่ยนตามเวลา หรือต่อเนื่องกันข้ามเดือน ต้องเลือกวิธีอนุมานให้ตรงกับโครงสร้างนั้น

ตามเอกสาร SciPy ค่า fit.pvalue ทดสอบ slope เท่ากับศูนย์ จึงใช้ตอบคำถามว่า alpha เท่ากับศูนย์หรือไม่โดยตรงไม่ได้ และการพบ alpha บวกในตัวอย่างก็ยังไม่แยกฝีมือ โชค ค่าธรรมเนียม และปัจจัยที่ขาดไปออกจากกัน

Beta เท่ากันยังมีความผันผวนต่างกันได้

กองทุนสองตัวอาจตอบสนองต่อตลาดเท่ากัน แต่มี residual ต่างขนาดกัน สำหรับ OLS ชุดนี้ residual ตั้งฉากกับตลาดในตัวอย่าง ทำให้เขียน sample variance ได้ว่า

sy2=β^2sx2+sε^2.s_y^2=\hat\beta^2s_x^2+s_{\hat\varepsilon}^2.

พจน์แรกคือความแปรปรวนส่วนที่เส้นตรงเชื่อมกับตลาด พจน์หลังคือส่วนที่เหลือ ถ้าจะใช้เป็นแบบจำลองความเสี่ยงในอนาคต ต้องมีข้อสมมติเรื่อง covariance ระหว่าง factor และ error เพิ่มเติม ความเท่ากันใน sample อย่างเดียวไม่เพียงพอ

fund_noisier = alpha + beta * x + 3 * residual
zero_beta_fund = 2 * residual
risk_comparison = pd.DataFrame({
    "Beta": [stats.linregress(x, z).slope for z in [y, fund_noisier, zero_beta_fund]],
    "Monthly SD": [np.std(z, ddof=1) for z in [y, fund_noisier, zero_beta_fund]],
}, index=["Original", "Larger residual", "Zero beta"])
print(risk_comparison.round(6))
print(np.isclose(np.var(y, ddof=1), beta ** 2 * np.var(x, ddof=1) + np.var(residual, ddof=1)))

[คำสั่ง for z in รายการ] เรียกว่า list comprehension ทำคำสั่งเดียวกันให้ข้อมูลแต่ละชุด ในตาราง beta สองแถวแรกเท่ากับ 1.2 แต่ SD ของแถวที่เพิ่ม residual สูงกว่า ส่วนแถว Zero beta มี beta ใกล้ศูนย์และ SD ประมาณ 0.008356 หรือ 0.8356% ต่อเดือน จึงยังผันผวนได้ np.isclose เทียบตัวเลขโดยยอมให้มีความต่างเล็กน้อยจากการคำนวณ และบรรทัดสุดท้ายได้ True

Beta ศูนย์หมายถึงความสัมพันธ์เชิงเส้นกับตลาดเป็นศูนย์ในชุดที่กำลังวัด เมื่อความแปรปรวนตลาดเป็นบวก ไม่ได้พิสูจน์ความเป็นอิสระทั้งหมด ความสัมพันธ์รูปโค้งอาจยังมีอยู่ และกองทุนอาจเสี่ยงต่อปัจจัยอื่นได้

CAPM กำหนดความสัมพันธ์ของผลตอบแทนคาดหวัง

การ fit เส้นตรงข้างต้นเป็นการอธิบายข้อมูลที่เกิดขึ้นแล้ว ส่วน Capital Asset Pricing Model หรือ CAPM เป็นทฤษฎีดุลยภาพที่เชื่อมความเสี่ยงตลาดกับผลตอบแทนคาดหวัง:

E[Ri]=Rf+βi(E[RM]−Rf).E[R_i]=R_f+\beta_i\bigl(E[R_M]-R_f\bigr).

E[⋅]E[\cdot] หมายถึงค่าคาดหวังก่อนผลลัพธ์เกิดขึ้น และ E[RM]−RfE[R_M]-R_f คือ market risk premium ที่คาดไว้ CAPM แบบพื้นฐานอาศัยเงื่อนไข เช่น นักลงทุนตัดสินใจด้วย mean–variance ในช่วงเวลาเดียวกัน มีความคาดหวังร่วมกัน ซื้อขายในตลาดที่ไม่มีแรงเสียดทานตามแบบจำลอง และกู้หรือให้กู้ที่อัตราปลอดความเสี่ยงเดียวกัน การเปลี่ยนเงื่อนไขอาจเปลี่ยนผลที่ได้

ในทฤษฎี MM เป็นพอร์ตสินทรัพย์เสี่ยงทั้งหมดในตลาด ส่วนดัชนีที่นำมาใช้จริงเป็น proxy หรือสิ่งที่ใช้แทน เราต้องบอกว่าใช้ดัชนีใด การ fit ตลาดหุ้นประเทศหนึ่งจึงไม่ได้ทดสอบ CAPM ทุกแง่มุมโดยอัตโนมัติ Sharpe อธิบายความต่างระหว่าง expected return กับผลตอบแทนที่สังเกตย้อนหลังไว้ในบทสัมภาษณ์เรื่อง CAPM

สมมติข้อมูลเข้าใหม่สำหรับหนึ่งปี: Rf=3%R_f=3\% และ market premium ที่คาดไว้ 5% หุ้น beta 1.2 จะมีผลตอบแทนคาดหวังตาม CAPM เท่ากับ 3%+1.2(5%)=9%3\%+1.2(5\%)=9\% นี่เป็นสถานการณ์สมมติใหม่ที่ใช้หน่วยรายปี ไม่ได้นำข้อมูล 12 เดือนข้างต้นมาอ้างว่าเป็นค่าพยากรณ์

rf_annual = 0.03
market_premium_annual = 0.05
scenario_betas = pd.Series({"Low": 0.5, "Market-like": 1.0, "High": 1.2, "Negative": -0.3})
capm_expected = rf_annual + scenario_betas * market_premium_annual
print((capm_expected * 100).round(2))

ได้ผลตอบแทนคาดหวัง 5.50%, 8.00%, 9.00% และ 1.50% ต่อปีตามลำดับ กรณี beta ติดลบจะได้ expected return ต่ำกว่า RF เมื่อ market premium เป็นบวกตามที่สมมติไว้ ผลในปีหนึ่งอาจต่างจากค่าคาดหวังมาก

เมื่อใช้ CAPM เป็นข้อจำกัดด้านการกำหนดผลตอบแทนคาดหวัง alpha ในประชากรที่เทียบกับตลาดตามทฤษฎีควรเป็นศูนย์ แต่ time-series regression ในข้อมูลจริงยังมี residual แต่ละงวดและ sampling error ได้ CAPM ไม่ได้กล่าวว่าผลตอบแทนหุ้นทุกเดือนต้องเท่ากับ beta คูณตลาดอย่างพอดี

ถ่วง beta ของพอร์ตได้เมื่อกำหนดน้ำหนักให้ตรง

ถ้าใช้ factor เดียวกัน หน่วยเดียวกัน ช่วงเวลาเดียวกัน และน้ำหนักพอร์ตคงที่ทุกงวด ความเป็นเส้นตรงทำให้ beta ของพอร์ตเท่ากับค่าเฉลี่ยถ่วงน้ำหนัก beta ของสมาชิก ลองสร้างอีกกองทุนให้ beta 0.5 แล้วผสมกองทุนเดิม 60% กับกองทุนใหม่ 40%

second_fund_excess = 0.0005 + 0.5 * x - 0.5 * residual
portfolio_excess = 0.6 * y + 0.4 * second_fund_excess
second_beta = stats.linregress(x, second_fund_excess).slope
portfolio_beta = stats.linregress(x, portfolio_excess).slope
print(f"Weighted beta: {0.6 * beta + 0.4 * second_beta:.4f}")
print(f"Fitted portfolio beta: {portfolio_beta:.4f}")

ได้ 0.9200 ทั้งสองวิธี คำนวณมือคือ 0.6(1.2)+0.4(0.5)=0.920.6(1.2)+0.4(0.5)=0.92 ตัวเลข 60/40 ในที่นี้เป็นน้ำหนักลงทุน ส่วน beta 1.2 และ 0.5 เป็นความไวของแต่ละกองทุน น้ำหนักลงทุนรวมหนึ่ง แต่ beta รวมไม่มีข้อบังคับว่าต้องเท่ากับหนึ่ง

การคูณ 60/40 ทุกเดือนสมมติว่าปรับน้ำหนักกลับต้นเดือน หากปล่อยให้มูลค่าพอร์ตเปลี่ยนแล้วน้ำหนักไหลตามราคา ความไวของพอร์ตอาจเปลี่ยนตามเวลา จะใช้สูตรน้ำหนักเริ่มต้นเดียวครอบทั้งช่วงโดยไม่ตรวจไม่ได้

หน่วยเปลี่ยนแล้วค่าใดเปลี่ยนตาม

เรารัน regression โดยใช้ 0.01 แทน 1% ถ้าเปลี่ยนทั้ง xx และ yy เป็นหน่วยเปอร์เซ็นต์ เส้นตรงยังชันเท่าเดิม แต่ intercept และ residual จะมีค่าตัวเลขใหญ่ขึ้น 100 เท่า เพราะหน่วยของผลตอบแทนเปลี่ยน

fit_percent = stats.linregress(100 * x, 100 * y)
print(f"Beta in decimal units: {beta:.4f}")
print(f"Beta in percentage units: {fit_percent.slope:.4f}")
print(f"Alpha as decimal: {alpha:.4f}")
print(f"Alpha in percentage points/month: {fit_percent.intercept:.4f}")

ได้ beta 1.2000 ทั้งคู่ และ alpha เปลี่ยนจาก 0.0010 เป็น 0.1000 ซึ่งหมายถึง 0.1 จุดเปอร์เซ็นต์ต่อเดือนเท่ากัน ถ้าคูณเฉพาะฝั่งกองทุนหรือเฉพาะฝั่ง factor ตัวเลข beta จะเปลี่ยนด้วย การเทียบ beta จากคนละไฟล์จึงต้องตรวจหน่วยทั้งสองฝั่ง

Beta ไม่ต้องคูณ 12 เพื่อเปลี่ยนเป็นรายปี ส่วนการรายงาน alpha รายเดือนคูณ 12 เป็นเพียงธรรมเนียม annualization เชิงเลขคณิต ต้องระบุให้ชัด ไม่ใช่ CAGR และไม่รับประกันว่า alpha จะสะสมได้ทุกเดือน การเปลี่ยนไป fit ข้อมูลรายปีจริงยังอาจให้ beta ต่างออกไป เพราะเปลี่ยนทั้งการรวมข้อมูลและจำนวน observations

ลองคำนวณก่อนอ่านเฉลย

1. ตลาดส่วนเกินเพิ่ม 2 จุดเปอร์เซ็นต์

สมมติ beta เท่ากับ 0.75 และปัจจัยอื่นในสมการคงเดิม ค่าที่เส้นตรงอธิบายให้กองทุนเปลี่ยนเท่าไร?

เฉลย: 0.75×2=1.50.75\times2=1.5 จุดเปอร์เซ็นต์ คำตอบนี้เป็นการเปลี่ยนของ fitted value ผลตอบแทนที่เกิดจริงยังมี residual

2. Alpha กับผลตอบแทนรวม

ถ้า RF เดือนนั้นเท่ากับ 0.2%, ตลาดส่วนเกิน −1%, alpha 0.1%, beta 1.2 และ residual −0.3% ผลตอบแทนรวมกองทุนเท่าไร?

เฉลย: 0.2+0.1+1.2(−1)−0.3=−1.2%0.2+0.1+1.2(-1)-0.3=-1.2\% การบวก RF เพียงครั้งเดียวเปลี่ยนผลตอบแทนส่วนเกินกลับเป็นผลตอบแทนรวม

3. กองทุน beta ศูนย์

เพื่อนบอกว่ากองทุน beta ศูนย์เหมาะใช้แทนเงินสดเพราะไม่มีความเสี่ยง ควรตรวจอะไรเพิ่ม?

เฉลย: ตรวจความผันผวนและการขาดทุนของ residual ความสัมพันธ์กับปัจจัยอื่น สภาพคล่อง และช่วงข้อมูลที่ใช้ประมาณ ตัวอย่าง Zero beta ในบทนี้ยังมี SD 0.8356% ต่อเดือน จึงสรุปจาก beta ศูนย์เพียงตัวเดียวไม่ได้

4. Alpha บวกหลังหักตลาดแล้ว

กองทุนได้ alpha บวกในการถดถอยปัจจัยตลาดหนึ่งตัว ข้อมูลนี้เพียงพอให้สรุปว่าเกิดจากฝีมือผู้จัดการหรือยัง?

เฉลย: ยังต้องประเมินความไม่แน่นอนของค่าประมาณ ค่าธรรมเนียม และปัจจัยที่อาจขาดไป บทถัดไปจะสร้างข้อมูลที่ alpha จากแบบจำลองตลาดเป็นบวก แล้วลดเหลือศูนย์เมื่อเพิ่มปัจจัยที่ใช้สร้างผลตอบแทนจริง

แหล่งเรียนและขอบเขตตัวอย่าง

อ่าน Transcript เต็มของ Introduction to factor investing และ Factor models and the CAPM เมื่อ 2 ตุลาคม 2026 ใช้เป็นโครงหัวข้อเรื่องปัจจัยร่วมและ CAPM คำอธิบายไทย ข้อมูล ตัวอย่าง Python และแบบฝึกหัดในหน้านี้เขียนขึ้นใหม่

เอกสารประกอบคือ SciPy: linregress สำหรับ API และความหมายของค่าที่คืนมา และ Revisiting the Capital Asset Pricing Model สำหรับคำอธิบายของ Sharpe เรื่องดุลยภาพและค่าคาดหวัง บทนี้แยกผลตอบแทนคาดหวังออกจากผลที่เกิดขึ้นแต่ละเดือน และไม่ใช้คำอธิบายเรื่อง premium เป็นข้อรับประกันผลตอบแทน

ค้นหาในหนังสือ

ปิด
คำค้นหา

© 2023 by QuantCorner, Thailand.

bottom of page