Showing posts with label ekholm. Show all posts
Showing posts with label ekholm. Show all posts

Friday, October 10, 2014

SVG + Javascript Ekholm Decomposition in RStudio Browser

Our topics this week seem unrelated, but in an effort to bridge the two

another random project – make website in R for these SVGs of Portland Vector Bridges
result: Portland Bridges in SVG
code: R to make simple site

Ekholm decomposition

SelectionShare & TimingShare | Masterfully Written by Delightfully Responsive Author
Popular Mutual Funds Decomposed With Ekholm (2014)

Responsive SVG in the browser

Responsive SVG in Your RStudio Browser
SVG + a little extra (d3.js) in RStudio Browser | No Pipes This Time

let’s build a website in R with htmltools to calculate the Ekholm decomposition in Javascript using this nifty simple-statistics.js from the brilliant Tom Macwright.  The result will not be beautiful and I’ll leave out a fancy interactive chart, but that is intentional to reduce the amount of code and dependencies.

r_ekholm_js

I wonder what I’ll get into next week.

Github Repo

library(htmltools)
library(pipeR)
library(jsonlite)
library(Quandl)
library(xts)

# use Quandl Kenneth French Fama/French factors
# http://www.quandl.com/KFRENCH/FACTORS_D
#f <- Quandl("KFRENCH/FACTORS_D",type = "xts", start_date="2010-12-31") / 100

tagList(
#pull in the bridge to span all the week's topics
#Portland Vector Bridges http://timelyportfolio.github.io/portland_vector_bridges
tags$div( style = "height:15%;width:100%"
,readLines(
"http://timelyportfolio.github.io/portland_vector_bridges/Burnside Bridge.svg"
) %>>% HTML
)
,tags$h1( "Sparsest Test in Javascript of Ekholm")
, tags$div( style = "width:100%"
,tags$div( style = "background-color:red;"
,"Note: Date range currently limited to one year, but there is a fairly easy workaround
for the next version."
)
,tags$div(
style = "display:inline-block; width: 25%;float:left;"
,"Mutual Fund Symbol", tags$input( id = "mfsymbol" )
,tags$br()
,"Start Date "
, tags$span( style="font-size:75%;fill:lightgray", "(2013-08-29)" )
, tags$input( type = "date", id = "stdate" )
,tags$br()
,"End Date"
,tags$span( style="font-size:75%;fill:lightgray", "(2014-08-29)" )
, tags$input( type = "date", id= "enddate" )
,tags$br()
,tags$input(
type="submit", id = "calc", value = "Calculate"
)
,tags$br()
)
, tags$div(style = "display:inline-block;height:100%;width:60%;margin-left:30px"
, tags$textarea(id = "results", style="width:100%; height:150px")
)
)
,tags$script(sprintf(
'

var french = %s;
'
, toJSON(data.frame("Date"=index(f),f)) %>>% HTML
))
,tags$script(
'

function calculateEkholm( data ) { // data in form of x,y or fund-rf, mkt-rf
/* get an error with regression.js
var myReg = regression(
"linear",
data
)
*/

// so use the great simple-statistics library
var myReg = ss.linear_regression().data(data);

//get residuals
var resid = data.map(function(p){return myReg.line()(p[0]) - p[1]});

//regress residuals^2 on (mkt-rf)^2
var myReg2 = ss.linear_regression().data(
data.map(function(d,i){
return [ Math.pow(d[0],2), Math.pow(resid[i],2) ]
})
)
//coefficients ^ 1/2 will give us ActiveAlpha and ActiveBeta
var activeAlpha = Math.pow( myReg2.b(), 0.5 );
var activeBeta = Math.pow( myReg2.m(), 0.5 );

//now do the next step to get ActiveShare and SelectionShare
var selectionShare = Math.pow(activeAlpha, 2 ) / ( ss.variance(data.map(function(d){return d[1]})) * (data.length - 1) / data.length )
var timingShare = Math.pow(activeBeta, 2 ) * ss.mean( data.map(function(d){return Math.pow(d[0],2)}) ) / ( ss.variance(data.map(function(d){return d[1]})) * (data.length - 1) / data.length )

//pass correlation result also
var correlation = ss.sample_correlation(data.map(function(d){return d[0]}),data.map(function(d){return d[1]}));

return {
regression: myReg,
correlation: correlation,
activeAlpha: activeAlpha,
activeBeta: activeBeta,
selectionShare: selectionShare,
timingShare: timingShare
}
}


// thanks https://gist.github.com/fincluster/6145995
function getStock(opts, type, complete) {
var defs = {
desc: false,
baseURL: "http://query.yahooapis.com/v1/public/yql?q=",
query: {
quotes: \'select * from yahoo.finance.quotes where symbol = \"{stock}\" | sort(field=\"{sortBy}\", descending=\"{desc}\")\',
historicaldata: \'select * from yahoo.finance.historicaldata where symbol = \"{stock}\" and startDate = \"{startDate}\" and endDate = \"{endDate}\"\'
},
suffixURL: {
quotes: "&env=store://datatables.org/alltableswithkeys&format=json&callback=?",
historicaldata: "&env=store://datatables.org/alltableswithkeys&format=json"
}
};

opts = opts || {};

if (!opts.stock) {
complete("No stock defined");
return;
}

var query = defs.query[type]
.replace("{stock}", opts.stock)
.replace("{sortBy}", defs.sortBy)
.replace("{desc}", defs.desc)
.replace("{startDate}", opts.startDate)
.replace("{endDate}", opts.endDate)

var url = defs.baseURL + query + (defs.suffixURL[type] || "");

return url;
}


d3.select("#calc").on("click",function(){
calculateFund(
d3.select("#mfsymbol")[0][0].value,
d3.select("#stdate")[0][0].value,
d3.select("#enddate")[0][0].value
)
})

function calculateFund( symbol, startdate, enddate ) {

d3.json(getStock({stock:symbol.toUpperCase(),startDate:startdate,endDate:enddate},"historicaldata"), function(e1,fund){


if( e1 || !fund.query.results ) {
updateResults ( {e1:e1, e2:e2, queryresults: "query problems"} );
} else {
var fund_factor = [];

//manipulate data to join fund with factors
//would be nice to have a xts merge in javascript


// query.results.quote will have the data stripped of meta
// also we will sort date ascending
fund = fund.query.results.quote
.sort(function(a,b){
return d3.ascending(
d3.time.format("%Y-%m-%d").parse(a.Date),
d3.time.format("%Y-%m-%d").parse(b.Date)
)
} );




// now lets go period by period with fund.map
fund.map( function(per, i){
if( i > 0 ) {
var frenchThisPer = french.filter(function(d){return d.Date == per.Date})[0];
fund_factor.push([
//Date: per.Date,
//FundPrice:
per.Adj_Close / fund[ i - 1 ].Adj_Close - 1 - frenchThisPer["RF"],
//Rm_Rf:
+frenchThisPer["Mkt.RF"],
//Rf: +frenchThisPer["RF"]/100
])
}
})

updateResults( calculateEkholm( fund_factor ) );
}

})
}

function updateResults( ekholmCalc ){
var ekhArr = [];
Object.keys(ekholmCalc).map(function(k){
ekhArr.push( [ k,": ", ekholmCalc[k] ].join("") )
})
d3.select("#results").text(ekhArr.join("\\n"))
}
'
%>>% HTML )
) %>>%
attachDependencies(
list(
htmlDependency(
name="d3"
,version="3.4"
,src=c("href"="http://d3js.org/")
,script="d3.v3.min.js"
)
,htmlDependency(
name="simple_statistics"
,version="0.1"
,src=c("href"=
"http://timelyportfolio.github.io/rCharts_factor_analytics/js"
)
,script = "simple_statistics.js"
)
)
) %>>% html_print

Monday, October 6, 2014

Popular Mutual Funds Decomposed With Ekholm (2014)

While we have a foundation and momentum from the last post “SelectionShare & TimingShare | Masterfully Written by Delightfully Responsive Author” , we can run the Ekholm calculations on some popular funds to see how they have evolved since the early 1980s.  Remember these are my opinions and not investment advice.  I chose these four funds for

  1. popularity in terms of Assets Under Management (AUM)
  2. style (active)
  3. tenure (old)
  4. evolution ( 2 evolved in a bad way and 2 have stayed consistent in a good way ).

This will continue what I envision to be a whole series of posts experimenting with the Ekholm (2014) decomposition into SelectionShare and TimingShare and determining how to use it in a mutual fund selection process.  Just as a reminder, below is the link to the very well-done paper by Anders Ekholm.

Ekholm, Anders G.

Components of Portfolio Variance: Systematic, Selection and Timing

August 8, 2014

http://ssrn.com/abstract=2463649

See if you can make any conclusions after reading the paper and looking at the chart below on the 2 year rolling Ekholm decomposition of FPA Crescent ®  Fund (FPACX), Sequoia ® Fund (SEQUX), Fidelity ® Contrafund ® (FCNTX), and The Growth Fund of America ®   (AGTHX).

image

As always, I really would like for you to reproduce and extend.  Please let me know if you do.  Below is the code.

# perform Ekholm (2012,2014) analysis on some popular mutual funds

# Ekholm, A.G., 2012
# Portfolio returns and manager activity:
# How to decompose tracking error into security selection and market timing
# Journal of Empirical Finance, Volume 19, pp 349-€“358

# Ekholm, Anders G., July 21, 2014
# Components of Portfolio Variance:
# R2, SelectionShare and TimingShare
# Available at SSRN: http://ssrn.com/abstract=2463649


library(Quandl) # use to get Fama/French factors
library(pipeR) # pipes are the future or R
library(rlist) # rlist - like underscore/lodash for R lists
library(dplyr) # super fast and really powerful
library(tidyr) # next gen wide/long formatter package
library(latticeExtra) # old but still awesome
library(directlabels) # fantastic and works with ggplot & lattice
library(quantmod) # also will load xts

# use Quandl Kenneth French Fama/French factors
# http://www.quandl.com/KFRENCH/FACTORS_D
f <- Quandl("KFRENCH/FACTORS_D",type = "xts") / 100

# grab our function from post
# http://timelyportfolio.blogspot.com/2014/10/selectionshare-timingshare-masterfully.html
devtools::source_gist("e5728c8c7fb45dbdb6e0")

tickers <- c( "FCNTX", "AGTHX", "SEQUX", "FPACX" )
ekFunds <- lapply(
tickers
,function(ticker) {
ticker %>>%
getSymbols( from = "1896-01-01", auto.assign = F ) %>>%
(fund ~
structure(
fund[,6] / stats::lag( fund[,6], 1 ) - 1
,dimnames = list(NULL,gsub(x = colnames(fund)[6], pattern = "[\\.]Adjusted", replacement = ""))
)
) %>>%
merge( f ) %>>% # Quandl("KFRENCH/FACTORS_D",type = "xts") / 100
na.omit %>>%
rollapply (
FUN= function(x){
x %>>%
jensen_ekholm %>>%
( data.frame( summary(.[["linmod"]])$"r.squared" , .$ekholm ) ) %>>%
xts(order.by=tail(index(x),1)) -> return_df
colnames(return_df)[1] <- "R_sq"
return(return_df)
}
, width = 500
#, by = 100
, by.column=F
, fill = NULL
) %>>%
na.fill(0)
}
)
names(ekFunds) <- tickers

ekFunds %>>%
list.map(
{
structure(
data.frame(
date = index(.)
, fund = names(ekFunds)[.i]
, .
)
) %>>%
gather(measure,value,-date,-fund)
}
) %>>%
(
do.call( rbind , . )
) -> ekT

ekT %>>%
# just plot at R^2, SelectionShare, and TimingShare
filter( measure %in% c("R_sq","SelectionShare","TimingShare") ) %>>%
(
xyplot(
value ~ date | measure
, groups = fund
, data = .
, type = "l"
# using direct.label so not necessary
, auto.key = list( space = "right" )
# title our plot
, main = paste(
"Comparison of Ekholm Decomposition for Various Mutual Funds"
,paste0("2 Year Rolling since ",format(.$date[1],format="%b %d, %Y"))
,sep="\n"
)
# layout one on top of the other
, layout = c(1,length(unique(.$measure)))
)
) %>>%
# I like labels on plot rather than legend
directlabels::direct.label( method = "last.qp" ) %>>%
# pretty it up with the latticeExtra Economist theme
asTheEconomist

Friday, October 3, 2014

SelectionShare & TimingShare | Masterfully Written by Delightfully Responsive Author

Anders Ekholm has written a wonderful paper

Ekholm, Anders G.

Components of Portfolio Variance: Systematic, Selection and Timing

August 8, 2014

http://ssrn.com/abstract=2463649

demonstrating how we might decompose a money manager’s performance with just a return stream.  Since his method relies simply on the return stream, he overcomes one of the biggest challenges of the Petajisto / Cremers’ related concepts of Active Share and Tracking Error which is the lack of data due to lagged infrequent reporting and high cost.  For other potential flaws in Active Share, see this post summarizing some very good PIMCO research.

Also, fortunately for me and maybe my faithful readers, the author has been very kind and responsive helping me translate his example Excel sheet into R.  Click on the screenshot below for the full details with replicable code and example with a real live mutual fund.

Of course, this is simply the beginning of what will be a wonderful journey exploring both the simplicity and complexity of Ekholm’s brilliance.

image