أكثر من سبب للحركة
لنموذج CAPM عامل واحد هو السوق. وفي 1992 بيّن فاما وفرينش أن عاملين آخرين يفسران جزءًا كبيرًا من فروق عوائد الأسهم: الحجم (ربحت الشركات الصغيرة أكثر من الكبيرة، عامل SMB أي الصغيرة ناقص الكبيرة) والقيمة (ربحت الأسهم الرخيصة بحسب نسبة القيمة الدفترية إلى السوقية أكثر من الغالية، عامل HML أي العالية ناقص المنخفضة). وأضافت نماذج لاحقة الزخم والربحية والاستثمار. وكل عامل هو نفسه سلسلة عوائد، وتعرّض الأصل له يقيس مدى حركته مع تلك السلسلة.
import numpy as np
rng = np.random.default_rng(1)
n = 250
market = rng.normal(0.0004, 0.010, size=n) # excess returns of three factors
smb = rng.normal(0.0001, 0.005, size=n)
hml = rng.normal(0.0001, 0.005, size=n)
factors = np.column_stack([market, smb, hml]) # shape (n, k) = (250, 3)
asset = 0.0002 + factors @ np.array([1.1, 0.5, -0.3]) + rng.normal(0, 0.004, size=n)
X = np.column_stack([np.ones(n), factors]) # design matrix: a column of ones for alpha
print(X.shape) # (250, 4)# The design matrix of three days and two factors
import numpy as np
factors = np.array([[0.010, -0.002], [-0.004, 0.001], [0.006, 0.003]])
X = np.column_stack([np.ones(len(factors)), factors])
print(X) # a column of ones, then market and SMBالمربعات الصغرى بصيغة المصفوفات
مع عدة عوامل يكون الانحدار asset = alpha + b1 * f1 + ... + bk * fk + error. نضع العوامل بجانب عمود من الآحاد في مصفوفة X، وتحل معاملات المربعات الصغرى المعادلات الطبيعية (X'X) c = X'y. وفي NumPy: np.linalg.solve(X.T @ X, X.T @ y). وهي الآلية نفسها لانحدار العامل الواحد، لكن الآن بـ k + 1 مجهولًا. المعامل الأول هو ألفا والبقية هي التعرضات.
import numpy as np
rng = np.random.default_rng(1)
n = 250
factors = np.column_stack([rng.normal(0.0004, 0.010, size=n), rng.normal(0.0001, 0.005, size=n), rng.normal(0.0001, 0.005, size=n)])
asset = 0.0002 + factors @ np.array([1.1, 0.5, -0.3]) + rng.normal(0, 0.004, size=n)
X = np.column_stack([np.ones(n), factors])
coef = np.linalg.solve(X.T @ X, X.T @ asset)
print(np.round(coef, 4)) # [alpha, market, SMB, HML]: the loadings come out near [1.1, 0.5, -0.3]
residual = asset - X @ coef
r2 = 1 - np.sum(residual ** 2) / np.sum((asset - asset.mean()) ** 2)
print(round(float(r2), 4))كم تفسر العوامل؟
يجيب معامل التحديد R²: حصة تغير الأصل التي تفسرها العوامل، 1 - SSres / SStot، بين 0 (لا شيء) و1 (كل شيء). وللمحفظة الأسهم المنوعة غالبًا R² فوق 0.9 على عوامل فاما-فرينش: فمخاطرها شبه كلها مخاطر عوامل. وللسهم المنفرد قيمة أقل، والباقي، التغير المتبقي، خاص بالشركة. وإضافة عامل لا تخفض R² داخل العينة أبدًا، فلا يثبت رقم أعلى وحده أن عاملًا مهم.
# What the model implies: the expected return from the loadings and the factor premia
import numpy as np
betas = np.array([1.1, 0.5, -0.3]) # market, SMB, HML loadings
premia = np.array([0.0006, 0.0002, 0.0002]) # expected daily factor returns
rf = 0.0001
expected = rf + np.sum(betas * premia)
print(round(float(expected), 6)) # 0.0008 per day, about 20% a year on 250 days- يُجري النموذج العاملي انحدار العوائد الزائدة على عدة عوامل:
X = [آحاد | عوامل]والمعاملات[alpha, beta_1..beta_k]. - حلّ المعادلات الطبيعية
np.linalg.solve(X.T @ X, X.T @ y)؛ و R² هو1 - SSres / SStotولا ينخفض أبدًا عند إضافة عامل. - يستلزم النموذج
rf + sum(betas * premia)؛ والتعرضات والعلاوات تقديرات، وقد تختفي علاوة العامل.
التالي: معامل المعلومات، قياس ما إذا كانت الإشارة تتنبأ بالعوائد أصلًا.
المختبر: ما الذي يُقيَّم
- جهّز الانحدار باستخدام
design_matrix25 نقطة - قدّر المعاملات باستخدام
fit_factor_model25 نقطة - قيّم جودة الملاءمة باستخدام
r_squared25 نقطة - سعّر التعرضات باستخدام
factor_expected_return25 نقطة