[R-SIG-Finance] Incorrect Formula in table.CAPM?

Brian G. Peterson brian at braverock.com
Fri Sep 17 20:47:39 CEST 2010


I'll take a look, though likely not in detail till Monday.
So, for now, some unsupported comments...

There are some difference3s of opinion in how to incorporate the risk 
free rate.  In Bacon, for example, the risk free rate is not used in the 
Tracking Error equations, but it is defined in the text as  as 'excess 
returns' (which incorporates the risk free rate).

Annualization is always a bit strange, and by defintion, it is an 
approximation. So, if you have real annual portfolio and benchmark 
returns, these would always be preferable to use than the annualized 
'scale' approximations based on number of observations in a year.  The 
annualization is mostly to make series of differeng lengths or scales 
comparable, with the attendant tradeoffs.

Your two numbers are also likely the same (or close enough) given the 
number of significant digits in your input data...

In the future, these types of reports are easier to debug with a fully 
reproducible example, e.g. drawn from the 'edhec' or 'managers' data sets.


   - Brian

On 09/17/2010 01:38 PM, Russ Devlin wrote:
> Brian,
> I have one other much smaller issue that I've noticed (in this case, I
> could easily be wrong, so feel free to tell me). I noticed that even
> after fixing the tracking error my spreadsheet-based information ratios
> were very slightly different than the ones calculated by table.CAPM. The
> difference seems to be in the roll-up to annualized returns used in
> calculating the active premium and how the risk free rate factors in.
> For example, if I enter in Excess Return as follows:
>     ActivePremium(bm_xts['1995-03-01::2010-06-30',"Composite..Gross."]-bm_xts["1995-03-01::2010-06-30",
>     "Monthly.Yield.on.3.Month.T.Bills"] ,
>     bm_xts['1995-03-01::2010-06-30',"Benchmark"]-bm_xts["1995-03-01::2010-06-30",
>     "Monthly.Yield.on.3.Month.T.Bills"])
> it gives me...
>     [1] 0.0231069
> which is what table.CAPM comes up with for Active Premium when the risk
> free series is specified. (I later figured out that I could do the RF
> rate explicitly in table.CAPM rather than subtracting from both
> portfolio and benchmark.) Meanwhile,
>     ActivePremium(bm_xts['1995-03-01::2010-06-30',"Composite..Gross."] ,
>     bm_xts['1995-03-01::2010-06-30',"Benchmark"])
> gives me
>     [1] 0.0238008
> which matches what I've been using. While I might have thought the
> results would be the same because the risk free rate is being subtracted
> from both the composite and benchmark, because it is annualized first
> the numbers are coming out slightly different. I don't think the
> Risk-Free rate should affect the ActivePremium (right?), as it is just
> the difference between the annualized returns of the portfolio and
> benchmark. If I put this code in table.CAPM, I match my numbers:
>     active.premium = (Return.annualized(Ra, scale = scale) -
>     Return.annualized(Rb, scale = scale))
> Again, thank you for all of your help.
> Best regards,
> Russ
> On Sep 17, 2010, at 9:16 AM, Brian G. Peterson wrote:
>> On 09/17/2010 07:43 AM, devlinme at mac.com <mailto:devlinme at mac.com> wrote:
>>> I've been getting up to speed on the Performance Analytics package,
>>> and there's one formula I'm having some trouble reconciling with some
>>> manual results I have done in spreadsheets. The table.CAPM function
>>> has a tracking error calculation that looks like this:
>>> tracking.error = sqrt(sum(merged.assets[, 1] - merged.assets[,
>>> 2])^2/(length(merged.assets[, 1]) - 1)) * sqrt(scale)
>>> This gives different results than the TrackingError function (which
>>> just uses the standard deviation function). I think the formula in
>>> table.CAPM should be this:
>>> tracking.error = sqrt(sum((merged.assets[, 1] - merged.assets[,
>>> 2])^2)/(length(merged.assets[, 1]) - 1)) * sqrt(scale)
>>> This result flows through to the Information Ratio as well.
>> I've always been uncomfortable with the code for this table. In an
>> attempt to make it run marginally faster, the calculations are done
>> inside the table. function rather than calling the underlying (and
>> more closely examined) standalone functions.
>> This introduces the strong possibility of cut and paste errors, or of
>> fixes or improvements from other parts of the code not propagating
>> properly into the table (as happened in this case, from a glance at
>> the commit logs).
>> Given my little rant above, I've changed the function to simply call
>> TrackingError(), and committed it as R-Forge r1763.
>> Thank you for the report. We appreciate the feedback and careful
>> commentary.
>> Regards,
>> - Brian
>> --
>> Brian G. Peterson
>> http://braverock.com/brian/
>> Ph: 773-459-4973
>> IM: bgpbraverock

Brian G. Peterson
Ph: 773-459-4973
IM: bgpbraverock

More information about the R-SIG-Finance mailing list