LemaireJean-Baptiste / eventstudy

Event Study package is an open-source python project created to facilitate the computation of financial event study analysis.
GNU General Public License v3.0
60 stars 41 forks source link

Carhart Four-Factor Model #9

Closed Azifer closed 3 years ago

Azifer commented 3 years ago

This is very helpful and easy to use – thank you!

I was wondering whether you could add the option of using the Carhart Four-Factor Model to modelise the returns. This is essentially the Fama-French Three-Factor Model with an additional factor, Momentum. Many research papers use this model so it would be extremely useful to have it.

Link to Carhart (1997): https://onlinelibrary.wiley.com/doi/10.1111/j.1540-6261.1997.tb03808.x

I am new to Python so am not sure how I could add this myself. Since it is similar to the Fama-French Three-Factor Model, I assume there is a way to edit that model by adding the Momentum data to it. One source of this data is https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/data_library.html, but probably best to calculate using code.

LemaireJean-Baptiste commented 3 years ago

Hello Azifer,

The package allows you to create your own functions (I need to write a documentation on this process, and to simplify it a bit).

Basically, you can supercharge the Single class with your own models' definition. You have to create a function that will return the data required to compute the event study (residuals, degree of freedom and model variance), and then you can either use the low-level api access to the Single class. Or better solution : plug the model to the Single class API, by overloading it with a new class method (see below).

You can try the code below, which implement the second option. It may have some issue (or not, I didn't test it). But send me some data and your code and we will be able to make it works together.

I used PR1YR as the variable name for the one-year return momentum (as defined in your paper), so you will have to use this name as column name in your factors' csv file, that you can import using the es.Single.import_FamaFrench() function.

Let me know if it works!

import numpy as np
import eventstudy as es
from eventstudy.models import Model
# import Model class, which implement the OLS regression logic 
# (you can also compute the OLS yourself using whatever packages you prefer)

# CARHART MODEL DEFINITION

def Carhart_4factor(
    security_returns,
    Mkt_RF,
    SMB,
    HML,
    PR1YR,
    RF,
    *,  # Named arguments only
    estimation_size: int,
    event_window_size: int,
    keep_model: bool = False,
    **kwargs
):
    """
    This function compute the regression using the Carhart 4 factor model, 
    it isolates the statistics logic. The aim is to return the data necessary to compute the eventstudy,
    namely the residuals, the degree of freedom and the variance of the model.
    """

    RF = np.array(RF)
    Mkt_RF = np.array(Mkt_RF)
    security_returns = np.array(security_returns)

    X = np.column_stack((Mkt_RF, SMB, HML, PR1YR))
    Y = np.array(security_returns) - np.array(RF)

    if keep_model:
        residuals, df, var_res, model = Model(estimation_size, event_window_size, keep_model).OLS(
            X, Y
        )

        var = [var_res] * event_window_size

        return residuals, df, var, model
    else:
        residuals, df, var_res = Model(estimation_size, event_window_size, keep_model).OLS(
            X, Y
        )

        var = [var_res] * event_window_size

        return residuals, df, var

# SINGLE API INJECTION

@classmethod
def Carhart_4factor_API(
    cls,
    security_ticker,
    event_date: np.datetime64,
    event_window: tuple = (-10, +10),
    estimation_size: int = 300,
    buffer_size: int = 30,
    keep_model: bool = False,
    **kwargs
):
    """
        This function create a bridge between the Single class API and the model definition function.
        It focuses on the data gathering process.
    """

    (security_returns,) = cls._get_parameters(
        "returns",
        (security_ticker,),
        event_date,
        event_window,
        estimation_size,
        buffer_size,
    )
    Mkt_RF, SMB, HML, PR1YR, RF = cls._get_parameters(
        "FamaFrench",
        ("Mkt-RF", "SMB", "HML", "PR1YR", "RF"),
        event_date,
        event_window,
        estimation_size,
        buffer_size,
    )

    description = f"Carhart 4-factor model estimation, Security: {security_ticker}"

    return cls(
        Carhart_4factor,
        {
            "security_returns": security_returns,
            "Mkt_RF": Mkt_RF,
            "SMB": SMB,
            "HML": HML,
            "PR1YR": PR1YR,
            "RF": RF,
        },
        event_window=event_window,
        estimation_size=estimation_size,
        buffer_size=buffer_size,
        keep_model=keep_model,
        description=description,
        event_date=event_date
    )

es.Single.Carhart_4factor = Carhart_4factor_API
# The method Carhart_4factor is injected in the Single Class, allowing this model 
# to be used like the other models natively supported by the package.

# USAGE

# You can import your factor using the es.Single.import_FamaFrench()
# https://lemairejean-baptiste.github.io/eventstudy/api/eventstudy.Single.import_FamaFrench.html#eventstudy.Single.import_FamaFrench
# (note to myself : this function should be renamed import_Factors)

es.Single.import_FamaFrench("carhart_factors.csv")

# You can then follow the docs on fama french, as the Carhart_4factor function is a copy-paste of these one,
# the usage will be the same
# https://lemairejean-baptiste.github.io/eventstudy/api/eventstudy.Single.FamaFrench_3factor.html#eventstudy.Single.FamaFrench_3factor

event = es.Single.Carhart_4factor(
    security_ticker = 'AAPL',
    event_date = np.datetime64('2007-01-09'),
    event_window = (-5,+20)
)
Azifer commented 3 years ago

This works and is extremely helpful.

Thanks a lot!