Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Quantitative Backtest Pipeline

BUFN736 - Fall 2025

A comprehensive quantitative backtest pipeline for factor-based portfolio construction and performance evaluation. This project implements a complete workflow from data loading through portfolio construction to performance analysis.

Overview

This notebook contains a standalone quantitative backtest pipeline that:

  1. Loads stock data from WRDS (CRSP, Compustat) and factor data from Open Asset Pricing (OSAP)
  2. Filters the investment universe based on market cap, price, exchange, and share codes
  3. Computes industry-adjusted z-scores for factors
  4. Selects factors using Fama-MacBeth cross-sectional regressions
  5. Constructs long/short portfolios based on selected factors
  6. Evaluates out-of-sample performance using CAPM and Carhart 4-factor models
  7. Generates comprehensive visualizations and summary statistics

Features

Data Loading

  • CRSP Monthly Data: Stock returns, prices, market capitalization, exchange codes
  • OSAP Factors: 110+ quantitative factors including:
    • Value factors (BM, EP, SP, etc.)
    • Profitability factors (GP, OperProf, roaq, etc.)
    • Growth/Investment factors (AssetGrowth, Investment, etc.)
    • Accruals, Capital Structure, Distress, Momentum, Liquidity, Risk, Analyst, and Size factors
  • Compustat Fundamentals: Annual financial statement data for custom factor construction

Universe Filtering

  • Minimum price filter ($5.00)
  • Minimum market cap filter ($100M)
  • Exchange restrictions (NYSE, AMEX, NASDAQ)
  • Common stock only (share codes 10, 11)

Factor Processing

  • Industry-adjusted z-scoring using Fama-French 49 industry classification
  • Multicollinearity filtering based on correlation thresholds
  • Core Factor Set Selection: Determines a consistent set of factors based on overall coverage (average coverage ≥60%, present in ≥70% of years)
  • Fama-MacBeth Estimation: Runs cross-sectional regressions using the same core factor set across all years (no floating factors)
  • Factor selection requires both significant t-statistics (|t-stat| ≥ 1.5) and sufficient temporal coverage (≥15 years)
  • In-sample period: 1985-2009
  • Out-of-sample period: 2010-2024

Portfolio Construction

  • Long/short portfolios based on top/bottom deciles of factor scores
  • Equal-weighted portfolios
  • Rebalanced annually at year-end

Performance Evaluation

  • Raw performance metrics (returns, volatility, Sharpe ratio)
  • CAPM alpha and beta
  • Carhart 4-factor model (Market, SMB, HML, UMD)
  • Information ratio
  • Year-by-year performance breakdown
  • Rolling statistics (12-month rolling Sharpe, volatility)

Requirements

Python Packages

pandas
numpy
statsmodels
pathlib
openassetpricing (oap)
wrds
openpyxl (for Excel file reading)
matplotlib (for visualizations)
seaborn (for visualizations)

Data Access

  • WRDS Account: Required for accessing CRSP and Compustat data
  • OSAP Access: Required for downloading factor signals

Required Files

  • SIC_49_Industry.xlsx - SIC to Fama-French 49 industry mapping (must be in project root)
  • ffdata.csv - Fama-French factor data (market, SMB, HML, UMD) (must be in project root)

Project Structure

Portfolio_Project/
├── main.ipynb                          # Main notebook with complete pipeline
├── README.md                           # This file
├── SIC_49_Industry.xlsx                # SIC to FF49 industry mapping
├── ffdata.csv                          # Fama-French factors
├── data/                               # Data directory (created automatically)
│   ├── crsp_monthly_raw.parquet       # Raw CRSP monthly data
│   ├── annual_returns.parquet         # Annual returns computed from CRSP
│   ├── december_snapshot.parquet      # December snapshots for universe filtering
│   ├── compustat_annual.parquet       # Compustat annual data
│   ├── osap_signals_december.parquet  # OSAP factors (December snapshots)
│   └── factors_with_zscores_filtered.parquet  # Processed factors with z-scores
└── output/                             # Output directory (created automatically)
    ├── selected_factors.csv            # Selected factors from Fama-MacBeth
    ├── selected_factors_summary.csv    # Summary of selected factors
    ├── portfolio_returns_monthly.csv   # Monthly portfolio returns
    ├── performance_results.csv         # Detailed performance metrics
    ├── performance_summary_table.csv   # Summary performance table
    ├── yearly_performance.csv          # Year-by-year performance
    ├── fama_macbeth_coefficients.csv   # Fama-MacBeth coefficients
    ├── fama_macbeth_statistics.csv     # Fama-MacBeth statistics
    ├── factor_correlation_matrix.csv   # Factor correlation matrix
    └── plots/                          # Visualization directory
        ├── cumulative_returns.png
        ├── monthly_returns.png
        ├── factor_performance.png
        ├── correlation_matrix.png
        └── rolling_statistics.png

Configuration

Key configuration parameters (defined in the notebook):

# Time periods
START_YEAR = 1985
END_YEAR = 2024
IN_SAMPLE_START = 1985
IN_SAMPLE_END = 2009
OUT_SAMPLE_START = 2010
OUT_SAMPLE_END = 2024

# Universe filters
MIN_PRICE = 5.0
MIN_MARKET_CAP = 100.0
VALID_EXCHANGES = [1, 2, 3]  # NYSE, AMEX, NASDAQ
VALID_SHARE_CODES = [10, 11]  # Common stock only

# Factor selection
INITIAL_FACTOR_COUNT = 110
FINAL_FACTOR_COUNT_MIN = 10
FINAL_FACTOR_COUNT_MAX = 20
MIN_T_STAT = 1.5
FACTOR_MIN_COVERAGE = 0.6

# Core factor set (for Fama-MacBeth)
CORE_MIN_AVG_COVERAGE = 0.6  # Minimum average coverage across years (60%)
CORE_MIN_YEARS_PRESENT = 0.7  # Minimum fraction of years present (70%)
MIN_YEARS_REQUIRED = 15        # Minimum years with valid data for final selection

# Portfolio construction
TOP_PERCENTILE = 0.10
BOTTOM_PERCENTILE = 0.10

Usage

Step 0: Setup & Configuration

  • Review and adjust configuration constants as needed
  • Ensure required data files are in the project root

Step 1: Data Loading

  • Load CRSP monthly data from WRDS
  • Load OSAP factor signals (December snapshots)
  • Optionally load Compustat data for custom factors
  • Data is saved to parquet files in data/ directory

Step 2: Universe Filtering

  • Apply price, market cap, exchange, and share code filters
  • Create filtered dataset for analysis

Step 2.5: Create Custom Factors (Optional)

  • Compute custom factors from Compustat data
  • Merge with existing OSAP factors

Step 3: Industry Z-Scoring

  • Map SIC codes to Fama-French 49 industries
  • Compute industry-adjusted z-scores for all factors

Step 3.5: Multicollinearity Check & Factor Filtering

  • Remove highly correlated factors
  • Filter factors based on coverage requirements

Step 4: Fama-MacBeth Estimation (In-Sample)

  • Determine Core Factor Set: Selects factors with average coverage ≥60% and present in ≥70% of years (1985-2009)
  • Fixed Factor Set: Uses the same core factor set in every yearly cross-sectional regression (no year-by-year factor filtering)
  • Missing Data Handling: Drops stocks (rows) with missing data, but keeps all core factors in the regression specification
  • Run cross-sectional regressions year-by-year (1985-2009) with consistent RHS variables
  • Factor Selection: Selects factors based on both t-statistics (|t-stat| ≥ 1.5) and minimum years requirement (≥15 years)
  • Generate factor selection summary with core set information

Step 5: Portfolio Construction (Out-of-Sample)

  • Construct long/short portfolios using selected factors (2010-2024)
  • Rebalance annually at year-end
  • Generate monthly portfolio returns

Step 6: Performance Evaluation

  • Compute raw performance metrics
  • Run CAPM and 4-factor regressions
  • Calculate risk-adjusted returns

Step 7: Save Results

  • Export all results to CSV files in output/ directory

Step 8: Visualizations & Summary Tables

  • Generate performance charts
  • Create correlation matrices
  • Produce summary tables

Key Classes

DataLoader

Handles data loading from WRDS and OSAP:

  • load_crsp_monthly(): Load CRSP monthly stock data
  • load_osap_signals_yearly(): Load OSAP factors as yearly snapshots
  • load_compustat_annual(): Load Compustat annual data

UniverseFilter

Filters investment universe:

  • filter_universe(): Apply price, market cap, exchange filters

IndustryZScore

Computes industry-adjusted z-scores:

  • map_sic_to_industry(): Map SIC codes to FF49 industries
  • compute_all_zscores(): Compute z-scores for all factors

FamaMacBeth

Runs Fama-MacBeth estimation with core factor set:

  • determine_core_factor_set(): Determines a consistent set of factors based on coverage criteria (avg coverage ≥60%, present in ≥70% of years)
  • estimate(): Runs Fama-MacBeth estimation using the core factor set across all years
  • select_factors(): Selects factors based on both t-statistics (|t-stat| ≥ 1.5) and minimum years requirement (≥15 years)
  • core_factors: Attribute storing the list of factors in the core set (accessible after estimate())

PortfolioConstructor

Constructs portfolios:

  • construct_portfolio(): Build long/short portfolios

PerformanceEvaluator

Evaluates performance:

  • evaluate(): Compute comprehensive performance metrics
  • print_results(): Display formatted results

Output Files

Performance Metrics

  • performance_summary_table.csv: Summary of all performance metrics
  • yearly_performance.csv: Year-by-year performance breakdown
  • portfolio_returns_monthly.csv: Monthly portfolio returns

Factor Analysis

  • selected_factors.csv: Complete list of selected factors
  • selected_factors_summary.csv: Summary statistics for selected factors
  • fama_macbeth_coefficients.csv: Fama-MacBeth coefficients by year
  • fama_macbeth_statistics.csv: Fama-MacBeth statistics
  • factor_correlation_matrix.csv: Correlation matrix of factors

Visualizations

All plots are saved in output/plots/:

  • Cumulative returns over time
  • Monthly returns distribution
  • Factor performance comparison
  • Factor correlation heatmap
  • Rolling performance statistics

Notes

  • The pipeline is designed to run end-to-end, but each step can be run independently if intermediate data files exist
  • Data files are saved in parquet format for efficient storage and loading
  • The notebook includes extensive error handling and progress reporting
  • All dates and years are clearly marked for in-sample vs. out-of-sample periods
  • Core Factor Set: The Fama-MacBeth estimation uses a fixed core factor set determined upfront, ensuring consistent regression specifications across all years. This prevents factors from "floating" in and out of the model based on year-by-year coverage, providing more stable and interpretable results.
  • Factor Selection Criteria: Final factor selection requires both statistical significance (|t-stat| ≥ 1.5) and sufficient temporal coverage (≥15 years out of 25 in-sample years), ensuring selected factors are both economically significant and consistently available.

License

This project is for academic use in BUFN736 - Fall 2025.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages