beta.md (3983B)
1 # Estimating Stock Betas 2 3 This describes the step by step procedure to estimate betas stock by stock first unconditionally and then using rolling windows. 4 We first download the monthly stock file. 5 6 7 Since we are about to download data from CRSP we set up a connection with our WRDS credentials 8 ```julia 9 using FinanceRoutines 10 using DataFrames, DataPipes, Dates 11 using FixedEffectModels # for regressions 12 13 const wrds_conn = FinanceRoutines.open_wrds_pg() 14 const date_init = Date("1990-01-01") 15 ``` 16 17 We are ready to import the monthly stock file: 18 ```julia 19 @time df_msf_raw = import_MSF_v2(wrds_conn; date_range = (date_init, Dates.today())); 20 ``` 21 22 And the Fama-French three pricing factors from Ken French's website. 23 This downloads directly data from Ken French's website and formats the data 24 ```julia 25 df_FF3 = import_FF3() 26 # make sure the returns are expressed in the same unit as in the MSF 27 transform!(df_FF3, [:mktrf, :smb, :hml, :rf] .=> ByRow((x->x/100)), renamecols=false ) 28 ``` 29 30 31 ## Unconditional Stock Betas 32 33 34 ### Format the monthly stock file from CRSP 35 36 ```julia 37 # keep only what we need from the MSF 38 df_msf = select(df_msf_raw, :permno, :mthcaldt => :date, :datem, 39 [:mthret, :mthcap] .=> ByRow(passmissing(Float64)) .=> [:ret, :mthcap]) # convert from decimals 40 ``` 41 42 43 ### Merge the data and estimate beta 44 45 ```julia 46 # Merge the data 47 df_msf = leftjoin(df_msf, df_FF3, on = [:datem] ) 48 # Create excess return 49 transform!(df_msf, [:ret, :rf] => ( (r1, r0) -> r1 .- r0 ) => :ret_rf) 50 51 # Estimate CAPM beta over the whole sample 52 sort!(df_msf, [:permno, :date]) 53 for subdf in groupby(df_msf, :permno) 54 if size(dropmissing(subdf, [:ret_rf, :mktrf]))[1] > 2 55 β_CAPM = coef(reg(subdf, @formula(ret_rf ~ mktrf)))[2] 56 subdf[:, :β_CAPM ] .= β_CAPM 57 else 58 subdf[:, :β_CAPM ] .= missing 59 end 60 end 61 select(unique(df_msf, [:permno, :β_CAPM]), :permno, :β_CAPM) 62 63 # Estimate 3 Factor betas 64 for subdf in groupby(df_msf, :permno) 65 if size(dropmissing(subdf, [:ret_rf, :mktrf, :smb, :hml]))[1] > 2 66 β_MKT, β_SMB, β_HML = coef(reg(subdf, @formula(ret_rf ~ mktrf + smb + hml)))[2:4] 67 subdf[:, :β_MKT ] .= β_MKT 68 subdf[:, :β_SMB ] .= β_SMB 69 subdf[:, :β_HML ] .= β_HML 70 else 71 subdf[:, :β_MKT ] .= missing; 72 subdf[:, :β_SMB ] .= missing; 73 subdf[:, :β_HML ] .= missing 74 end 75 end 76 unique(df_msf, r"β") 77 select(unique(df_msf, r"β"), :permno, :β_MKT, :β_SMB, :β_HML) 78 ``` 79 80 81 82 ## Rolling Betas for Stocks 83 84 I export a very simple function for rolling betas (see the test for examples). 85 86 First we prepare the basic dataset from the monthly stock file and the Fama-French risk factors for example 87 ```julia 88 # Get individual stock returns 89 df_msf = select(df_msf_raw, :permno, :mthcaldt => :date, :datem, 90 [:mthret, :mthcap] .=> ByRow(passmissing(Float64)) .=> [:ret, :mthcap]) # convert from decimals 91 # merge and create excess returns 92 df_msf = leftjoin(df_msf, df_FF3, on = [:datem] ) 93 transform!(df_msf, [:ret, :rf] => ( (r1, r0) -> r1 .- r0 ) => :ret_rf) 94 sort!(df_msf, [:permno, :date]) 95 ``` 96 97 Now we are ready to run the regression using the function `calculate_rolling_betas` that the package exports 98 ```julia 99 insertcols!(df_msf, :a=>missing, :bMKT=>missing, :bSMB=>missing, :bHML=>missing) 100 @time for subdf in groupby(df_msf, :permno) 101 β = calculate_rolling_betas( 102 [ones(nrow(subdf)) subdf.mktrf subdf.smb subdf.hml], 103 subdf.ret_rf; 104 window=60, # 60 months 105 min_data=nothing, # what is the minimum number of nonmissing data to return a proper number 106 method=:linalg 107 ) 108 subdf[!, [:a, :bMKT, :bSMB, :bHML]] = β 109 end 110 111 import Statistics: median, mean 112 @p df_msf |> groupby(__, :datem) |> 113 combine(__, :bMKT .=> 114 [(x-> mean(skipmissing(x))) (x-> median(skipmissing(x)))] .=> 115 [:bMKT_mean :bMKT_median]) 116 ``` 117 Go make some coffee ... this takes a little while (~ 15mn on M2max macbook pro). 118 I don't think my method is super efficient 119 120 121