AbnormalReturns API

This package reexports StatsModels.jl and BusinessDays.jl. See those packages and their respective documentation for methods that they export.

Notable methods from StatsModels.jl:

  • @formula

Notable methods from BusinessDays.jl:

  • advancebdays
  • bdayscount
  • tobday

Setting Up Data

AbnormalReturns.MarketDataType
function MarketData(
    df_market,
    df_firms;
    date_col_market=:date,
    date_col_firms=:date,
    id_col=:permno,
    add_intercept_col=true,
    valuecols_market=nothing,
    valuecols_firms=nothing
)

Arguments

  • df_market: A Tables.jl compatible source that stores market data, indexed by date. The dates must be a unique set. The column name for the date column is specified by the keyword argument "datecolmarket"
  • df_firms: A Tables.jl compatible source that stores firm data. Each firm must have a unique set of dates. The column name for the date column is specified by the keyword argument "datecolfirms" and the firm ID column is specified by the keyword argument "id_col"
  • valuecols_market=nothing: If left as nothing, all other columns in df_market are used as the value columns. These are the columns that are stored in the resulting dataset. Otherwise a vector of Symbol or String specifying column names.
  • valuecols_firms=nothing: Same as above
  • id_col=:permno: The column corresponding to the set of firm IDs in df_firms
  • add_intercept_col=true: Whether to add a column to the data for an intercept (which is always equal to 1)

MarketData is the main data storage structure. Data is stored for each firm in a Dict, where the data itself is a NamedTuple (names corresponding to column names, such as "ret"), and the keys for the Dict corresponding to firm IDs. The MarketData struct also stores overall market data and a calendar of dates.

Any firm data must have a corresponding market data date, so there cannot be a firm return if there is not a market return on that date.

Example

df_firm = CSV.File(joinpath(data_dir, "daily_ret.csv"))
df_mkt = CSV.File(joinpath(data_dir, "mkt_ret.csv"))
df_mkt[!, :mkt] = df_mkt.mktrf .+ df_mkt.rf
mkt_data = MarketData(
    df_mkt,
    df_firm
)
source
AbnormalReturns.all_unique_obsFunction

Checks whether each firmid-date pair is unique, assumes that vectors are sorted by firmid then date

Returns true if there is at least one firm_id-date pair repeated, false if all are unique

source
AbnormalReturns.quick_regFunction
quick_reg(
    data::FixedTable,
    f::FormulaTerm;
    minobs::Real=0.8,
    save_residuals::Bool=false
)

quick_reg(
    data::IterateFixedTable,
    f::FormulaTerm;
    minobs::Real=0.8,
    save_residuals::Bool=false
)

Calculates a linear regression for the supplied data based on the formula (formula from StatsModels.jl). Unless the formula explicitly excludes the intercept (i.e., @formula(y ~ 0 + x)), an intercept is added.

If data is of the type IterateFixedTable, then the function uses the maximum number of threads on each FixedTable in an optimized way and returns a Vector{BasicReg}.

Arguments

  • minobs::Real: The minimum number of observations to return a completed regression. If less than 1, the value is used as a percentage relative to the total number of business days in the time period. Therefore, the default of 0.8 corresponds to at least 80% of the business days over the time period have values.
  • save_residuals::Bool=false: Whether to save the residuals into BasicReg, This can have significant performance implications.
source
AbnormalReturns.BasicRegType
function BasicReg(
    resp::AbstractVector,
    pred::AbstractMatrix,
    yname::String,
    xnames::SVector{N, String},
    f::FormulaTerm{L,R};
    save_residuals::Bool=false,
    minobs=1
)::BasicReg{L,R} where {L,R}

Arguments

  • resp::AbstractVector{Float64}: The "Y" or response in a linear regression
  • pred::AbstractMatrix{Float64}: The "X" matrix in a linear regression
  • yname::String: The name of the response variable
  • xnames::SVector{N, String}: The names of the prediction variables
  • f::FormulaTerm{L,R}: A StatsModels.jl formula, saved in the resulting struct
  • save_residuals::Bool=false: Whether or not to save the vector of residuals from the regression. Note for large numbers of regressions this can significantly slow down the speed
  • minobs::Int=1: The minimum length of the response vector for the regression to run. The regression will also not run if the length of the response vector is less than or equal to the number of columns in the prediction matrix.

BasicReg is an intentionally simplistic linear regression. It also attempts to produce a minimum number of allocations if views of vectors are passed.

source
AbnormalReturns.alphaFunction
alpha(rr::RegressionModel, coefname::String...="intercept")

"alpha" in respect to the the CAPM model, i.e., the intercept in the model. This is the alpha from the estimation period.

This function finds the position of the coefficient name provided, defaults to "intercept". If the coefname is not in the regression, then this function returns an error.

source
AbnormalReturns.betaFunction
beta(rr::RegressionModel, coefname::String...=["mkt", "mktrf", "vwretd", "ewretd"])

"beta" in respect to the CAPM model, i.e., the coefficient on the market return minus the risk free rate. This is the beta from the estimation period.

This function finds the position of the coefficient name provided, defaults to several common market returns. If the coefname is not in the regression, then this function returns an error.

source
Statistics.varFunction
var[std](rr::Union{AbstractVector{<:RegressionModel}, RegressionModel})
var[std](data::Union{IterateFixedTable, FixedTable}; minobs=0.8)

If a regression model is passed, then this calculates the variance (standard deviation) based on the residual sum of squares divided by the degrees of freedom. A vector of RegressionModel will return the same length of vector results.

If a FixedTable is passed (or an IterateFixedTable), and that contains only one column, then the variance (standard deviation) is calculated for that column. If it has two columns, then the calculation is based on the difference between the columns.

source

Calculation Functions

AbnormalReturns.bharFunction
bhar(
    data::FixedTable{T, 2};
    minobs=0.8
) where {T}

bhar(
    data::FixedTable,
    rr::RegressionModel;
    minobs=0.8
)

bhar(
    data::IterateFixedTable{T, 2};
    minobs=0.8
) where {T, MNames, FNames}

bhar(
    data::IterateFixedTable,
    rrs::AbstractVector{<:BasicReg};
    minobs=0.8
)

Calculates the difference between buy and hold returns (also referred to as geometric returns) for a firm and a benchmark. If a regression is passed, then the benchmark is based on the coefficients from that regression and the performance of the benchmarks in the regression. These are sometimes called Fama-French abnormal returns. If no regression is passed, abnormal returns are calculated as the difference between the first and second columns in the FixedTable (second column is typically the benchmark such as the S&P 500 or a value weighted return of all firms).

Similar to constructing the regression, passing an IterateFixedTable will return a Vector and uses a more optimized method.

source
AbnormalReturns.carFunction
car(
    data::FixedTable{T, 2};
    minobs=0.8
) where {T}

car(
    data::FixedTable,
    rr::RegressionModel;
    minobs=0.8
)

car(
    data::IterateFixedTable{T, 2};
    minobs=0.8
) where {T, MNames, FNames}

car(
    data::IterateFixedTable,
    rrs::AbstractVector{<:BasicReg};
    minobs=0.8
)

Calculates the cumulative returns of a firm over a benchmark (through addition of each return). If a regression is passed, then the benchmark is based on the coefficients from that regression and the performance of the benchmarks in the regression. These are sometimes called Fama-French abnormal returns. If no regression is passed, abnormal returns are calculated as the difference between the first and second columns in the FixedTable (second column is typically the benchmark such as the S&P 500 or a value weighted return of all firms).

Similar to constructing the regression, passing an IterateFixedTable will return a Vector and uses a more optimized method.

source
AbnormalReturns.bh_returnFunction

Calculates the buy and hold returns (also called geometric return).

These functions treat missing returns in the period implicitly as a zero return.

source