《量化金融R语言高级教程》一2.2 在R中建模

简介:

本节书摘来异步社区《量化金融R语言高级教程》一书中的第2章,第2.2节,作者: 【匈牙利】Edina Berlinger(艾迪娜•伯林格) , 等 译者: 高蓉 责编: 胡俊英,更多章节内容可以访问云栖社区“异步社区”公众号查看。

2.2 在R中建模

在接下来的部分中,我们将会学习在R的帮助下如何实现先前讲过的模型。

2.2.1 数据选择

在第4章大数据—高级分析中,我们将会详细讨论获取开源数据及对其进行高效处理的各种知识与方法。在这里,我们仅仅讲述股票价格时间序列和其他相关信息如何获取和如何用于因素模型估计。

我们使用quantmod包来收集数据库。

以下是在R中实现的代码:

library(quantmod)
stocks <- stockSymbols()

然后,我们需要等待几秒钟获取数据,接着可以看到输出:

Fetching AMEX symbols...
Fetching NASDAQ symbols...
Fetching NYSE symbols...

现在,我们得到一个R数据框对象,这个对象涵盖了在各个交易所(如AMEX,NASDAQ,或者NYSE)进行交易的大约6500只股票。为了查看数据集涵盖的变量,我们用str命令:

str(stocks)
'data.frame': 6551 obs. of 8 variables: 
 $ Symbol  : chr "AA-P" "AAMC" "AAU" "ACU" ...
 $ Name   : chr "Alcoa Inc." "Altisource Asset Management Corp"...
 $ LastSale : num 87 1089.9 1.45 16.58 16.26 ...
 $ MarketCap: num 0.00 2.44e+09 9.35e+07 5.33e+07 2.51e+07 ...
 $ IPOyear  : int NA NA NA 1988 NA NA NA NA NA NA ...
 $ Sector  : chr "Capital Goods" "Finance" "Basic Industries"...
 $ Industry : chr "Metal Fabrications" "Real Estate"...
 $ Exchange : chr "AMEX" "AMEX" "AMEX" "AMEX" ...

我们可以删掉确实不需要的变量,也可以增加来自不同数据库的市场资本化信息和公司账面价值作为新变量,这些变量我们需要用来估计Fama-French模型:

stocks[1:5, c(1, 3:4, ncol(stocks))]
   Symbol LastSale MarketCap BookValuePerShare
1  AA-P  87.30     0     0.03
2  AAMC  985.00 2207480545    -11.41
3  AAU   1.29   83209284     0.68
4  ACU   16.50   53003808   10.95
5  ACY   16.40   25309415   30.13
我们也会需要无风险收益率的时间序列。我们通过计算月度LIBOR市场上的美元利率确定这个序列:

library(Quandl)
Warning message:
package 'Quandl' was built under R version 3.1.0
LIBOR <- Quandl('FED/RILSPDEPM01_N_B',
start_date = '2010-06-01', end_date = '2014-06-01')
Warning message:
In Quandl("FED/RILSPDEPM01_N_B", start_date = "2010-06-01", end_date
= "2014-06-01") : It would appear you aren't using an authentication
token. Please visit http://www.quandl.com/help/r or your usage may be
limited.

因为数据依然分配给LIBOR变量,我们可以忽略这个警告信息。

Quandl包,tseries包和其他收集数据的包会在第4章(大数据—高级分析)中详细讨论。

这个方法也可以用来获取股票价格,并且标普500指数(S&P500指数)可以用来表示市场组合。

我们有一张表,记录了接近5000只股票价格在2010年6月1日到2014年6月1日之间的时间序列。其中第一列和最后几列如下所示:

d <- read.table("data.csv", header = TRUE, sep = ";")
d[1:7, c(1:5, (ncol(d) - 6):ncol(d))]
    Date  SP500 AAU  ACU  ACY ZMH ZNH ZOES ZQK ZTS ZX
1 2010.06.01 1070.71 0.96 11.30 20.64 54.17 21.55 NA 4.45 NA NA
2 2010.06.02 1098.38 0.95 11.70 20.85 55.10 21.79 NA 4.65 NA NA
3 2010.06.03 1102.83 0.97 11.86 20.90 55.23 21.63 NA 4.63 NA NA
4 2010.06.04 1064.88 0.93 11.65 18.95 53.18 20.88 NA 4.73 NA NA
5 2010.06.07 1050.47 0.97 11.45 19.03 52.66 20.24 NA 4.18 NA NA
6 2010.06.08 1062.00 0.98 11.35 18.25 52.99 20.96 NA 3.96 NA NA
7 2010.06.09 1055.69 0.98 11.90 18.35 53.22 20.45 NA 4.02 NA NA

如果我们的数据已经储存在硬盘中,那么可以使用read.table函数方便的读取。在第4章(大数据—高级分析)中,我们会讨论如何从互联网收集数据。

现在,我们得到了所需数据:市场组合(标普500)、股票价格、无风险利率(月度LIBOR)。

为了清洗数据库,我们删除了有缺失值、0价格或者负价格的变量。最简单的实现如下:

d <- d[, colSums(is.na(d)) == 0]
d <- d[, c(T, colMins(d[, 2:ncol(d)]) > 0)]

为了使用colMins函数,需要应用matrixStats包。现在,我们可以开始处理数据。

2.2.2 通过主成分分析估计APT

由于识别影响证券收益率的宏观变量很困难(Medvegyev-Szaz,2010,pp.42),我们在实践中使用因子分析相当不易。通常,我们通过主成分分析来寻找驱动收益率变动的潜因子。

在最初下载的6500只股票中,我们可以使用4500只股票数据。其他部分由于缺失值或者0价格的缘故删除了。现在,由于这里用不到日期,还有我们将标普500本身视为一个独立的因子,在主成分分析(principal component analysis,PCA)中不考虑,我们又删除了前两列数据。然后,计算对数收益率:

p <- d[, 3:ncol(d)]
r <- log(p[2:nrow(p), ] / p[1:(nrow(p) - 1), ])

也有其他方法可以计算给定资产的对数收益率,就是PerformanceAnalytics库中的return.calculate(data,method="log")函数。

因为我们的股票数目过于庞大,为了实施PCA,要么需要数据时间长度至少25年,要么需要减少股票数量。使因素模型在几十年间保持稳定,这是不可能的。因此,为了达到说明的目的,我们随机选择了百分之十的股票,并对这个样本计算PCA模型:

r <- r[, runif(nrow(r)) < 0.1]

runif(nrow(r)) < 0.1给出了一个4013维的0—1向量,选出了原表中几乎10%的列(在这个例子中,个数为393)。我们也可以使用以下样本函数,在网站http://stat.ethz.ch/R-manual/ R-devel/library/base/html/sample.html上你可以找到更多细节:

pca <- princomp(r)

结果,我们得到一个princomp类对象。这个对象有8个属性,其中最重要的属性是加载矩阵和sdev属性(包括组成部分的标准差)。第一主成分是数据集方差最大的向量。

我们确认一下主成分的标准差:

plot(pca$sdev)

结果如图2-1所示。


2_1


我们可以看出,前5个成分是独立的。因此,我们应该选择5个因子。但是,其他因子的标准差同样显著。所以,不能通过几个因子解释整个市场。

我们可以通过调用factanal函数确认结果。这个函数估计了五因子模型:

factanal(r, 5)

我们可以发现,实施这个计算花费了很多时间。因子分析与PCA相关,但从数学角度看稍复杂一些。结果,我们得到一个factanal类对象。它有很多属性。但是,此时我们仅对以下输出部分有兴趣:

Factor1 Factor2 Factor3 Factor4 Factor5
SS loadings  56.474 23.631 15.440 12.092 6.257
Proportion Var 0.144 0.060  0.039 0.031 0.016
Cumulative Var 0.144 0.204  0.243 0.274 0.290
Test of the hypothesis that 5 factors are sufficient.
The chi square statistic is 91756.72 on 75073 degrees of freedom.The
p-value is 0

结果显示,五因子模型适合数据。但是,可解释的方差仅仅接近30%。这意味着模型应该考虑扩展进其他因子。

2.2.3 Fama-French模型估计

我们有一个包含4015只股票5年价格的数据框架,和包含LIBOR时间序列的LIBOR数据框架。首先,我们需要计算收益率,再与LIBOR利率合并。

第一步,我们删掉数学计算不需要的数据。然后,对保留下来的每一列计算对数收益率:

d2 <- d[, 2:ncol(d)]
d2 <- log(tail(d1, -1)/head(d1, -1))

在计算了对数收益率之后,我们把日期放回到收益率中。然后,在最后一步合并两个数据集:

d <- cbind(d[2:nrow(d), 1], d2)
d <- merge(LIBOR, d, by = 1)

需要提醒读者的是,对数据框架实施merge函数,相当于SQL中的(内)联语句。

结果如下:

print(d[1:5, 1:5])]
  Date LIBOR   SP500         AAU       ACU
2010.06.02 0.4    0.025514387  -0.01047130   0.034786116
2010.06.03 0.4    0.004043236   0.02083409    0.013582552
2010.06.04 0.4   -0.035017487  -0.04211149  -0.017865214
2010.06.07 0.4   -0.013624434   0.04211149   -0.017316450
2010.06.08 0.4   0.010916240   0.01025650   -0.008771986

我们将LIBOR利率转换为日度收益率:

d$LIBOR <- d$LIBOR / 36000

由于LIBOR利率的报价基于货币市场基差——以及(实际天数/360)的天数计算约定——而且时间序列包含使用百分比表示的利率,我们把LIBOR除以36000。现在,我们需要计算Fama-French模型的3个变量。正如在数据选择部分中所讲,我们有股票的数据框:

d[1:5, c(1,(ncol(d) - 3):ncol(d))]
 Symbol LastSale MarketCap BookValuePerShare
1 AA-P  87.30     0    0.03
2 AAMC 985.00    2207480545   -11.41
3 AAU  1.29  83209284    0.68
4 ACU  16.50  53003808    10.95
5 ACY  16.40  25309415    30.13

我们得删掉那些没有价格数据的股票:

> stocks = stocks[stocks$Symbol %in% colnames(d),]

我们将市场上限作为一个变量。我们仍需对每只股票计算账面市值比:

stocks$BookToMarketRatio <-
 stocks$BookValuePerShare / stocks$LastSale
str(stocks)
'data.frame': 3982 obs. of 5 variables:
 $ Symbol    : Factor w/ 6551 levels "A","AA","AA-P",..: 14
72...
 $ LastSale    : num 1.29 16.5 16.4 2.32 4.05 ...
 $ MarketCap   : num 8.32e+07 5.30e+07 2.53e+07 1.16e+08...
 $ BookValuePerShare: num 0.68 10.95 30.13 0.19 0.7 ...
 $ BookToMarketRatio: num 0.5271 0.6636 1.8372 0.0819 0.1728 ...

现在,我们需要计算SMB因素和HML因素。为了简化,我们将BIG公司定义为大于平均水平的公司。账面市值比实施同样的原则:

avg_size <- mean(stocks$MarketCap)
BIG <- as.character(stocks$Symbol[stocks$MarketCap > avg_size])
SMALL <- as.character(stocks[stocks$MarketCap < avg_size,1])

这些数组包括了BIG公司和SMALL公司。现在,我们可以定义SMB因素:

d$SMB <- rowMeans(d[,colnames(d) %in% SMALL]) –
 rowMeans(d[,colnames(d) %in% BIG])

我们接着定义HML因素:

avg_btm <- mean(stocks$BookToMarketRatio)
HIGH <- as.character(
 stocks[stocks$BookToMarketRatio > avg_btm, 1])
LOW <- as.character(
 stocks[stocks$BookToMarketRatio < avg_btm, 1])
d$HML <- rowMeans(d[, colnames(d) %in% HIGH]) –
 rowMeans(d[, colnames(d) %in% LOW])

第三个因素这样计算:

d$Market <- d$SP500 - d$LIBOR

定义完3个因素,我们在花旗集团(Citi)股票和伊克塞利克斯(EXEL)股票上试一试:

d$C <- d$C - d$LIBOR
model <- glm( formula = "C ~ Market + SMB + HML" , data = d)

GLM(general linear model,一般线性模型)函数作用如下:它将数据和公式作为参数读入。公式是一个形为响应~条件的字符串,其中响应是数据框中的一个变量名,条件指定了模型中的预测子,它包含在数据集中通过操作符“+”分隔开的变量名。这个函数也可以用于Logistic回归,只是缺省状态设定为线性。

模型输出如下:

Call: glm(formula = "C~Market+SMB+HML", data = d)
Coefficients:
(Intercept)  Market     SMB   HML
  0.001476   1.879100  0.401547 -0.263599
Degrees of Freedom: 1001 Total (i.e. Null); 998 Residual
Null Deviance:  5.74
Residual Deviance: 5.364  AIC: -2387

模型概要输出如下:

summary(model)
Call:
glm(formula = "C~Market+SMB+HML", data = d)
Deviance Residuals:
  Min   1Q  Median   3Q   Max
-0.09344 -0.01104 -0.00289 0.00604 2.26882
Coefficients:
          Estimate Std. Error t value Pr(>|t|)
(Intercept)   0.001476 0.002321 0.636  0.525
Market     1.879100 0.231595 8.114 1.43e-15 ***
SMB      0.401547 0.670443 0.599  0.549
HML     -0.263599 0.480205 -0.549  0.583
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
(Dispersion parameter for gaussian family taken to be 0.005374535)
  Null deviance: 5.7397 on 1001 degrees of freedom
Residual deviance: 5.3638 on 998 degrees of freedom
AIC: -2387
Number of Fisher Scoring iterations: 2

结果显示,唯一显著的因素是市场溢价。这表明花旗集团的股票收益率倾向于与整个市场本身共同变动。

使用以下命令可以画出结果:

estimation <- model$coefficients[1]+
 model$coefficients[2] * d$Market +
 model$coefficients[3]*d$SMB +
 model$coefficients[4]*d$HML
plot(estimation, d$C, xlab = "estimated risk-premium",
 ylab = "observed riks premium",
 main = "Fama-French model for Citigroup")
lines(c(-1, 1), c(-1, 1), col = "red")

图2-2绘出了对花旗集团的Fama-French模型估计的风险溢价。


2_2

看图2-2可以发现,收益率中存在一个异常值。我们将这个异常值设为0,看看不考虑它之后的结果:

outlier <- which.max(d$C)
d$C[outlier] <- 0

如果我们运行相同的代码建立模型,并再次计算收益率的估计值和观测值,得到以下结果:

model_new <- glm( formula = "C ~ Market + SMB + HML" , data = d)
summary(model_new)
Call:
glm(formula = "C ~ Market + SMB + HML", data = d)
Deviance Residuals:
   Min    1Q   Median   3Q   Max
-0.091733 -0.007827 -0.000633 0.007972 0.075853
Coefficients:
        Estimate Std. Error t value Pr(>|t|)
(Intercept) -0.0000864  0.0004498 -0.192 0.847703
Market    2.0726607 0.0526659 39.355 < 2e-16 ***
SMB     0.4275055 0.1252917  3.412 0.000671 ***
HML     1.7601956 0.2031631  8.664 < 2e-16 ***
---
Signif. codes: 0‘***’0.001‘**’0.01‘*’0.05‘.’0.1‘ ’1
(Dispersion parameter for gaussian family taken to be 0.0001955113)
  Null deviance: 0.55073 on 1001 degrees of freedom
Residual deviance: 0.19512 on 998 degrees of freedom
AIC: -5707.4
Number of Fisher Scoring iterations: 2

根据以上结果,所有3个因素均显著。

GLM函数不返回{{R}^{2}}。lm函数对线性回归同样可以得到精确值。我们可以从模型总结中读出r.squared = 0.6446。

结果显示,变量可以解释花旗集团风险溢价中超过64%的变动。我们绘出新结果:

estimation_new <- model_new$coefficients[1]+
 model_new$coefficients[2] * d$Market +
 model_new$coefficients[3]*d$SMB +
 model_new$coefficients[4]*d$HML
dev.new()
plot(estimation_new, d$C, xlab = "estimated risk-premium",ylab =
"observed riks premium",main = "Fama-French model for Citigroup")
lines(c(-1, 1), c(-1, 1), col = "red")

这个例子的输出如图2-3所示。


2_3

我们再检验另一只股票EXEL:

d$EXEL <- d$EXEL–d$LIBOR
model2 <- glm( formula = "EXEL~Market+SMB+HML" , data = d)
Call: glm(formula = "EXEL~Market+SMB+HML", data = d)
Coefficients:
(Intercept)   Market    SMB     HML
 -0.001048  2.038001  2.807804  -0.354592
Degrees of Freedom: 1001 Total (i.e. Null); 998 Residual
Null Deviance:  1.868
Residual Deviance: 1.364  AIC: -3759

模型小结输出如下:

summary(model2)
Call:
glm(formula = "EXEL~Market+SMB+HML", data = d)
Deviance Residuals:
   Min  1Q  Median    3Q  Max
-0.47367 -0.01480 -0.00088 0.01500 0.25348
Coefficients:
        Estimate Std. Error t value Pr(>|t|)
(Intercept) -0.001773  0.001185 -1.495 0.13515
Market    1.843306  0.138801 13.280 < 2e-16 ***
SMB     2.939550  0.330207  8.902 < 2e-16 ***
HML    -1.603046  0.535437 -2.994 0.00282 **
---
Signif. codes: 0‘***’0.001‘**’0.01‘*’0.05‘.’0.1‘ ’1

(Dispersion parameter for gaussian family taken to be 0.001357998)
   Null deviance: 1.8681 on 1001 degrees of freedom
Residual deviance: 1.3553 on 998 degrees of freedom
AIC: -3765.4
Number of Fisher Scoring iterations: 2

根据以上结果,所有3个因子均显著。

GLM函数并不包括{{R}^{2}}。但lm函数对线性模型也可以得到精确的结果。我们从模型小结中得到r.squared = 0.2723。根据这个结果,我们认为变量可以解释EXEL风险溢价的超过27%的变动。

使用以下命令可以绘出图2-4:

estimation2 <- model2$coefficients[1] +
 model2$coefficients[2] * d$Market +
 model2$coefficients[3] * d$SMB + model2$coefficients[4] * d$HML
plot(estimation2, d$EXEL, xlab = "estimated risk-premium",
 ylab = "observed riks premium",
 main = "Fama-French model for EXEL")
lines(c(-1, 1), c(-1, 1), col = "red")


2_4

相关文章
|
6月前
R语言分布滞后线性和非线性模型DLM和DLNM建模应用| 系列文章
R语言分布滞后线性和非线性模型DLM和DLNM建模应用| 系列文章
|
6月前
【R语言实战】——带有高斯新息的金融时序的GARCH模型拟合预测及VAR/ES风险度量
【R语言实战】——带有高斯新息的金融时序的GARCH模型拟合预测及VAR/ES风险度量
|
6月前
【R语言实战】——带有新息为标准学生t分布的金融时序的GARCH模型拟合预测
【R语言实战】——带有新息为标准学生t分布的金融时序的GARCH模型拟合预测
|
6月前
|
数据可视化
【R语言实战】——金融时序分布拟合
【R语言实战】——金融时序分布拟合
|
6月前
【R语言实战】——fGARCH包在金融时序上的模拟应用
【R语言实战】——fGARCH包在金融时序上的模拟应用
|
3月前
|
数据采集 存储 数据可视化
R语言时间序列分析:处理与建模时间序列数据的深度探索
【8月更文挑战第31天】R语言作为一款功能强大的数据分析工具,为处理时间序列数据提供了丰富的函数和包。从数据读取、预处理、建模到可视化,R语言都提供了灵活且强大的解决方案。然而,时间序列数据的处理和分析是一个复杂的过程,需要结合具体的应用场景和需求来选择合适的方法和模型。希望本文能为读者在R语言中进行时间序列分析提供一些有益的参考和启示。
|
6月前
|
数据可视化
【R语言实战】——金融时序ARIMA建模
【R语言实战】——金融时序ARIMA建模
|
6月前
|
机器学习/深度学习 数据可视化 数据挖掘
R语言逻辑回归logistic对ST股票风险建模分类分析混淆矩阵、ROC曲线可视化
R语言逻辑回归logistic对ST股票风险建模分类分析混淆矩阵、ROC曲线可视化
|
6月前
|
存储 数据挖掘
R语言用GARCH模型波动率建模和预测、回测风险价值 (VaR)分析股市收益率时间序列
R语言用GARCH模型波动率建模和预测、回测风险价值 (VaR)分析股市收益率时间序列
|
6月前
|
数据采集 人工智能 算法
R语言ARMA-GARCH模型金融产品价格实证分析黄金价格时间序列
R语言ARMA-GARCH模型金融产品价格实证分析黄金价格时间序列