Initialize

# load libs
  library(tidyquant)
  library(broom)

# set test data set
  tidyquant_test_data <- tibble(id = c(1,2),
                                data = list(tibble ( code = c("WIKI/AAPL.11","WIKI/MSFT.11"),
                                                     symbol = c("AAPL","MSFT")),
                                            tibble ( code = c("WIKI/AAPL.11","WIKI/F.11"),
                                                     symbol = c("AAPL","F"))
                                            ),
                                rebalance = c(NULL,TRUE)
                                )
  
# set your quandl api key like so
  # tidyquant::quandl_api_key("your key")

Functions

get_ff_factors <- function( p_factor ,
                              p_collapse,
                              p_momentum,
                              p_start_date,
                              p_end_date ) {

  # get 3 factors
    if(p_factor == 3 ){

      # get 3 factors
        switch( p_collapse,
                annual  =  q_code <- "KFRENCH/FACTORS_A" ,
                monthly =  q_code <- "KFRENCH/FACTORS_M" ,
                weekly  =  q_code <- "KFRENCH/FACTORS_W" ,
                daily   =  q_code <- "KFRENCH/FACTORS_D",
                stop("Please provide either monthly, weekly or daily as input")
               )
    }
  # get 5 factor model  
    else{
        switch( p_collapse,
                annual  =  q_code <- "KFRENCH/FACTORS5_A" ,
                monthly =  q_code <- "KFRENCH/FACTORS5_M" ,
                weekly  =  q_code <- "KFRENCH/FACTORS5_W" ,
                daily   =  q_code <- "KFRENCH/FACTORS5_D",
                stop("Please provide either monthly, weekly or daily as input")
        )
    }
    
  # make the call
    ff <- q_code %>%
          tidyquant::tq_get( get        = "quandl",
                             collapse   = p_collapse,
                             start_date = p_start_date,
                             end_date   = p_end_date)
  # get momentum
    if(p_momentum){
      
      ## select the correct code
         switch( p_collapse,
                  annual  =  q_code <- "KFRENCH/MOMENTUM_A",
                  monthly =  q_code <- "KFRENCH/MOMENTUM_M",
                  weekly  =  q_code <- "KFRENCH/MOMENTUM_W",
                  daily   =  q_code <- "KFRENCH/MOMENTUM_D",
                stop("Please provide either monthly, weekly or daily as input")
               )
      
      ## get the momentum and merge with factors
         ff <- q_code %>%
               tidyquant::tq_get( get        = "quandl",
                                  collapse   = p_collapse,
                                  start_date = p_start_date,
                                  end_date   = p_end_date) %>%
               dplyr::left_join(ff, by = "date")
    }
    
    return(ff)

}

###### GET DATA FOR FF ANALYSIS

  get_ff_from_quandl <- function( tic_data ,
                                  factor_model = 3 , 
                                  momentum = FALSE,
                                  weights = NULL ,
                                  rebalance = NULL ,
                                  start_date = "2007-01-01",
                                  end_date = "2015-12-31",
                                  aggregate = "monthly" ){
    
    
    ## hack for nested data
       tic_data <- tic_data[[1]]
    
    
    # check for proper input params
      ## check aggregate
         if ( ! aggregate %in% c("daily","weekly","monthly","annual"))
         stop("Please supply one of the following: daily, weekly, monthly, annual ")

      ## dates
         if ( is.null(start_date) | is.null(end_date))
         stop("Please supply start or end date in YYYY-MM-DD format")
    
      ## check rebalance
         if(!is.null(rebalance)){
           switch(  rebalance,
                    annual  =  rebalance <- "years",
                    monthly =  rebalance <- "months",
                    weekly  =  rebalance <- "weeks",
                    daily   =  rebalance <- "days",
                    rebalance = NA)
         } else {rebalance = NA }

  # get fama french factors and momentum
    fama_french_factors <- get_ff_factors( p_factor     = factor_model,
                                           p_collapse   = aggregate,
                                           p_momentum   = momentum, 
                                           p_start_date = start_date, 
                                           p_end_date   = end_date )
    
    
  # check weights and assign
    if(is.null(weights)){
      ## equal weights vector
         n <- nrow(tic_data)
         weights <- rep( ( 1 / n  ) , n )
    }
    
    
  # compute the portfolio returns
    portfolio <- tic_data %>%
                 tq_get(get        = "quandl",
                        transform  = "rdiff",
                        collapse   = aggregate,
                        start_date = start_date,
                        end_date   = end_date) %>%
                rename(monthly.returns = adj.close) %>%
                tq_portfolio(assets_col   = symbol,
                             returns_col  = monthly.returns,
                             rebalance_on = rebalance,
                             weights      = weights,
                             col_rename   = "portfolio.monthly.return")
    
 
    
  # join with portfolio  
    response <- dplyr::left_join(portfolio, fama_french_factors, by = "date") %>%
                mutate(excess.returns  = portfolio.monthly.return - rf )
    
  }

Get Data

  tidyquant_result_data <- tidyquant_test_data %>%
                           group_by(id) %>%
                           do( result = get_ff_from_quandl(tic_data = .$data,
                                                           rebalance = .$rebalance))
## Warning: No Quandl API key detected. Limited to 50 anonymous calls per day.
## Set key with 'quandl_api_key()'.

## Warning: No Quandl API key detected. Limited to 50 anonymous calls per day.
## Set key with 'quandl_api_key()'.

## Warning: No Quandl API key detected. Limited to 50 anonymous calls per day.
## Set key with 'quandl_api_key()'.

## Warning: No Quandl API key detected. Limited to 50 anonymous calls per day.
## Set key with 'quandl_api_key()'.

Run FF regression

 tidy_ff_regression <- tidyquant_result_data %>%
                        unnest(result) %>%
                        group_by(id) %>%
                        do(model = lm( excess.returns ~ `mkt.rf` + smb + hml , data=.)) 
  
  tidy_ff_params <- tidy_ff_regression %>% 
                    tidy(model)
  
  tidy_ff_params
## Source: local data frame [8 x 6]
## Groups: id [2]
## 
##      id        term      estimate   std.error   statistic      p.value
##   <dbl>       <chr>         <dbl>       <dbl>       <dbl>        <dbl>
## 1     1 (Intercept) -0.0477128032 0.011861632 -4.02244859 1.100181e-04
## 2     1      mkt.rf  0.0153491481 0.002794747  5.49214320 2.877600e-07
## 3     1         smb -0.0026026196 0.005522681 -0.47126019 6.384516e-01
## 4     1         hml -0.0045076376 0.004661981 -0.96689316 3.358623e-01
## 5     2 (Intercept) -0.0460966815 0.013548047 -3.40245954 9.521037e-04
## 6     2      mkt.rf  0.0192941174 0.003192087  6.04435871 2.423986e-08
## 7     2         smb  0.0019692232 0.006307862  0.31218552 7.555307e-01
## 8     2         hml -0.0001717479 0.005324793 -0.03225438 9.743316e-01
  tidy_ff_summary <- tidy_ff_regression %>% 
                     glance(model)
  
  tidy_ff_summary
## Source: local data frame [2 x 12]
## Groups: id [2]
## 
##      id r.squared adj.r.squared     sigma statistic      p.value    df
##   <dbl>     <dbl>         <dbl>     <dbl>     <dbl>        <dbl> <int>
## 1     1 0.2409526     0.2188444 0.1203925  10.89880 2.810667e-06     4
## 2     2 0.3143246     0.2943535 0.1375092  15.73895 1.694554e-08     4
## # ... with 5 more variables: logLik <dbl>, AIC <dbl>, BIC <dbl>,
## #   deviance <dbl>, df.residual <int>