Multi factor model

The multi factor model can be used to decompose returns and calculate risk. The factors are constructed using pricing, fundamental, and analyst estimates data. I will use Systematic Investor Toolbox for this section.

The gzcon() function creates a connection and reads data in compressed format. Once we create a connection, we also have to close the connection.

The following commands explain this:

> con = gzcon(url('http://www.systematicportfolio.com/sit.gz', 'rb'))
>  source(con)
> close(con)

The following function is used to fetch Dow Jones components data from http://money.cnn.com and join() is taken from Systematic Investor Toolbox:

>dow.jones.components<- function(){
url = 'http://money.cnn.com/data/dow30/'
   txt = join(readLines(url))
   temp = gsub(pattern = '">', replacement = '<td>', txt, perl = TRUE)
   temp = gsub(pattern = '</a>', replacement = '</td>', temp, perl = TRUE) 
   temp = extract.table.from.webpage(temp, 'Volume', has.header = T)
   trim(temp[,'Company']) }

The next single line of code is a call to the preceding function, which extracts the Dow Jones constituent list:

>tickers = dow.jones.components()

The following commands explain how to extract fundamental data for the last 80 months of all companies in the tickers list. These commands will take a few minutes to extract data so it is recommended to save the data once you extract it and later you should use the load() command to load it:

>data.fund<- new.env()
>   temp = paste(iif( nchar(tickers) <= 3, 'NYSE:', 'NASDAQ:'), tickers, sep='')
>for(i in 1:len(tickers)) data.fund[[tickers[i]]] = fund.data(temp[i], 80)
>save(data.fund, file='data.fund.Rdata')
# load(file='data.fund.Rdata')

The next set of commands is the same as preceding code, but to extract price data:

# get pricing data
>data <- new.env()
>getSymbols(tickers, src = 'yahoo', from = '1970-01-01', env = data, auto.assign = T)
>for(i in ls(data)) data[[i]] = adjustOHLC(data[[i]], use.Adjusted=T)    
>save(data, file='data.Rdata')
#load(file='data.Rdata')

The subsequent function creates various date variables in date format:

>date.fund.data<- function(data){
quarter.end.date = as.Date(paste(data['quarter end date',], '/1', sep=''), '%Y/%m/%d')  
quarterly.indicator = data['quarterly indicator',]
date.preliminary.data.loaded = as.Date(data['date preliminary data loaded',], '%Y-%m-%d') + 1
months = seq(quarter.end.date[1], tail(quarter.end.date,1)+365, by='1 month') 
index = match(quarter.end.date, months)
quarter.end.date = months[ iif(quarterly.indicator == '4', index+3, index+2) + 1 ] - 1
fund.date = date.preliminary.data.loaded
fund.date[is.na(fund.date)] = quarter.end.date[is.na(fund.date)] 
return(fund.date) }

Now you have extracted price and fundamental data, you should use this data to construct various fundamental factors such as EPS, number of shares outstanding, market capitalization, market value to book value, and so on. This loop calculates fundamental factors for every ticker one by one and creates a list:

> library(quantmod)
>for(i in tickers) {
fund = data.fund[[i]]
fund.date = date.fund.data(fund)          
# Earnings per Share        
EPS = get.fund.data('Diluted EPS from Total Operations', fund, fund.date, is.12m.rolling=T)                   
# Common Shares Outstanding
CSHO = get.fund.data('total common shares out', fund, fund.date)
# Common Equity
CEQ = get.fund.data('total equity', fund, fund.date)
# merge
data[[i]] = merge(data[[i]], EPS, CSHO, CEQ) }

Next, I filtered the preceding data for the period starting at 1995 and lasting at 2011:

>bt.prep(data, align='keep.all', dates='1995::2011')

Prices for all tickers can be extracted and NAN can be replaced with previous values using the following commands:

>     prices = data$prices
>     prices = bt.apply.matrix(prices, function(x) ifna.prev(x))

Now you have to construct fundamental ratios using fundamental factors and prices. I created three ratios; however, you can create any number of ratios that you would like to consider. I created market capitalization, EPS to price ratio, and book value to price:

# Financial Ratios
>factors$TV = list()
# Market Value - capitalization
> CSHO =  bt.apply(data, function(x) ifna.prev(x[, 'CSHO']))
> MKVAL = prices * CSHO
 #  Earnings / Price
> EPS = bt.apply(data, function(x) ifna.prev(x[, 'EPS']))
>factors$TV$EP = EPS / prices
#  Book Value / Price
> CEQ = bt.apply(data, function(x) ifna.prev(x[, 'CEQ']))
>factors$TV$BP = CEQ / MKVAL

As scaling for all of these ratios might be different, before moving on, we shouldn't forget to standardize it. I calculated the Z score to standardize this data:

# normalize (convert to z scores) cross sectional all Traditional Value factors
>for(i in names(factors$TV)) {
factors$TV[[i]] = (factors$TV[[i]] - 
cap.weighted.mean(factors$TV[[i]], MKVAL)) / 
                     apply(factors$TV[[i]], 1, sd, na.rm=T)
}
This is how we bind different data in multidimensional case
# compute the overall Traditional Value factor
>load.packages('abind') 
> temp = abind(factors$TV, along = 3)

Calculate the average of all the normalized factors:

>factors$TV$AVG = factors$TV[[1]]
>factors$TV$AVG[] = apply(temp, c(1,2), mean, na.rm=T)

As of now, we have daily data and created financial ratios on a daily basis. You can convert it to any frequency you desire. I converted it to monthly frequency and extracted data for the last day of the month:

# find month ends
>month.ends = endpoints(prices, 'months')
> prices = prices[month.ends,]
>     n = ncol(prices)
>nperiods = nrow(prices)

This is how you should calculate monthly return and its lag:

> ret = prices / mlag(prices) - 1
>next.month.ret = mlag(ret, -1)

Marker capitalization at the end of every month can be calculated using the following:

> MKVAL = MKVAL[month.ends,]

Extract all ratios for the last day of the month:

>for(j in 1:len(factors)) {  
for(i in 1:len(factors[[j]])) {
         factors[[j]][[i]] = factors[[j]][[i]][month.ends,]  
     }}

Next you should calculate quantiles, which can be calculated using the following commands. I created five quantiles, and the average next month return of each quantile is calculated using the earning price factor. Quantiles are created month by ranging stocks based on EP factor:

> out = compute.quantiles(factors$TV$AVG, next.month.ret, plot=F) 
> models = list()
>for(i in 1:5) {
data$weight[] = NA
data$weight[month.ends,] = iif(out$quantiles == i, out$weights, 0)
         capital = 100000
data$weight[] = (capital / prices) * (data$weight)  
     models[[paste('Q',i,sep='')]] = bt.run(data, type='share', capital=capital) }

The top and bottom are very extreme and should be used to create a spread (Q5 - Q1). The dynamics of this spread helps to design and develop an investing strategy, that is, momentum or mean reverting:

# spread
>data$weight[] = NA
>data$weight[month.ends,] = iif(out$quantiles == 5, out$weights, 
iif(out$quantiles == 1, -out$weights, 0))
>     capital = 100000
>data$weight[] = (capital / prices) * (data$weight)
> models$Q5_Q1 = bt.run(data, type='share', capital=capital)

Now you should run cross-sectional regression to estimate alpha and portfolio loadings and these can be calculated using the following commands:

>factors.avg = list()
>for(j in names(factors)) factors.avg[[j]] = factors[[j]]$AVG
>factors.avg = add.avg.factor(factors.avg)
>nperiods = nrow(next.month.ret)
> n =ncol(next.month.ret)
# create matrix for each factor
>factors.matrix = abind(factors.avg, along = 3)  
>all.data = factors.matrix
> # betas
> beta = all.data[,1,] * NA
# append next.month.ret to all.data
>all.data = abind(next.month.ret, all.data, along = 3)
>dimnames(all.data)[[3]][1] = 'Ret'
# estimate betas (factor returns)
>for(t in 30:(nperiods-1)) {
     temp = all.data[t:t,,]
     x = temp[,-1]
     y = temp[,1]
     beta[(t+1),] = lm(y~x-1)$coefficients
 }
 # create Alpha return forecasts
> alpha = next.month.ret * NA
>for(t in 40:(nperiods-1)) {
     # average betas over the last 6 months
coef = colMeans(beta[(t-5):t,],na.rm=T)
    alpha[t,] = rowSums(all.data[t,,-1] * t(repmat(coef, 1,n)), na.rm=T)    }

We can also use these alpha and beta to estimate future portfolio return as well.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset