Regression Analysis for Financial Relationships › A Complete Regression Workflow

Michael BrenndoerferDecember 11, 20252 min read
Quantitative FinanceAll chapters
Part 8 of 15Econometrics and Statistical Modeling
Chapter 43Regression Analysis for Financial Relationships › A Complete Regression Workflow
Reading section 8 of 12 · A Complete Regression Workflow · 2 minSection 8 of 12 · 2 min

Book contents

Section 8 of 12 · 2 min read

Regression Analysis for Financial Relationships › A Complete Regression WorkflowOne section · Sections in this chapter8 / 12 ↓

Let's demonstrate a complete workflow for estimating and validating a factor model, incorporating all the diagnostic and inference techniques covered.

In[35]:
Code
from scipy import stats


def complete_regression_analysis(y, X, variable_names=None):
    """
    Perform comprehensive regression analysis with diagnostics.

    Parameters:
    -----------
    y : array-like
        Dependent variable
    X : array-like
        Independent variables (without constant)
    variable_names : list, optional
        Names for the X variables

    Returns:
    --------
    dict : Dictionary containing model results and diagnostics
    """
    if variable_names is None:
        variable_names = [
            f"X{i}" for i in range(X.shape[1] if len(X.shape) > 1 else 1)
        ]

    # Convert to DataFrame if needed
    if isinstance(X, np.ndarray):
        X = pd.DataFrame(X, columns=variable_names)

    # Add constant
    X_const = sm.add_constant(X)

    # Fit models with different standard errors
    model_ols = sm.OLS(y, X_const).fit()
    model_hac = sm.OLS(y, X_const).fit(cov_type="HAC", cov_kwds={"maxlags": 4})

    # Diagnostics
    residuals = model_ols.resid

    # Durbin-Watson
    dw = durbin_watson(residuals)

    # Breusch-Pagan
    bp_stat, bp_pval, _, _ = het_breuschpagan(residuals, X_const)

    # Jarque-Bera (normality)
    jb_stat, jb_pval = stats.jarque_bera(residuals)

    results = {
        "model_ols": model_ols,
        "model_hac": model_hac,
        "durbin_watson": dw,
        "breusch_pagan": (bp_stat, bp_pval),
        "jarque_bera": (jb_stat, jb_pval),
        "residuals": residuals,
    }

    return results
In[36]:
Code
# Apply to our three-factor data
analysis = complete_regression_analysis(
    ff_df["excess_return"], ff_df[["MKT", "SMB", "HML"]], ["MKT", "SMB", "HML"]
)
Out[37]:
Console
Complete Regression Analysis
============================================================

1. COEFFICIENT ESTIMATES
------------------------------------------------------------
Variable     Coef         OLS SE       HAC SE      
------------------------------------------------------------
const        0.001172     0.000570     0.000554    
MKT          1.082611     0.045050     0.045832    
SMB          0.394167     0.067886     0.068394    
HML          -0.332426    0.081125     0.072631    

2. MODEL FIT
------------------------------------------------------------
R-squared:          0.5526
Adjusted R-squared: 0.5499
F-statistic:        205.88 (p = 6.19e-87)

3. DIAGNOSTIC TESTS
------------------------------------------------------------
Durbin-Watson:      1.9696  [OK]
Breusch-Pagan:      2.9407 (p = 0.4009)  [OK]
Jarque-Bera:        5.3142 (p = 0.0702)  [OK]

The diagnostic summary quickly flags potential issues. In this case, we look for "WARNING" flags that would necessitate using robust standard errors or model re-specification.