Skip to content

Repository files navigation

Reproducible Multi-Factor Equity Research

CI License: MIT

WenqiDing-CompFin maintains this educational Python project for auditable, monthly cross-sectional equity research.

This is the flagship research repository in the portfolio. The separate Quant Factor Lab is its interactive demo; use only this repository as the primary application or CV link. It contains the formal methodology, failure register, tests, real-data validation path, and reproducible artifacts.

Synthetic-data boundary: the default dataset is simulated and deliberately links returns to latent value, quality, and momentum structure. The included performance and IC values validate the research pipeline; they are not historical-market evidence, market alpha, or an investable result. Nothing in this repository is investment advice.

Official momentum portfolio spread across fixed reporting periods

Evidence Map

Evidence track Data What it establishes What it does not establish
Stock-level research pipeline Deterministic synthetic panel Timing, ranking, portfolio accounting, transaction costs, failure behavior, and reproducibility Historical alpha or an investable strategy
Public factor validation Official Kenneth French U.S. factor portfolios Real market factor regimes, long-history summary statistics, dependence-aware inference, and subperiod stability Performance of this repository's composite stock strategy
Characteristic-sorted portfolios Official value-weighted U.S. momentum deciles Whether prior-return sorting remains monotonic and statistically stable across fixed reporting periods A security-level reconstruction, feasible execution, or prospective discovery

Public Real-Market Check

The repository now includes an official current-return snapshot and a reproducible downloader for the Kenneth French Data Library. As reported by the source for May 2026:

Factor May 2026 Last 3 months Last 12 months
Market excess 4.90% 9.42% 25.63%
Size (SMB) -2.77% -1.89% 6.14%
Value (HML) -2.15% 0.42% 14.53%
Profitability (RMW) -8.42% -15.98% -27.50%
Investment (CMA) -1.39% -5.28% -2.92%

The mixed signs are intentional evidence, not a presentation problem: factor returns are regime-dependent. Run the full official monthly-history validation:

python run_public_factor_validation.py --start-date 1990-01-01

The script records source URLs and archive SHA-256 hashes and reports fixed subperiods plus a six-lag Newey-West t-statistic. See the public real-data protocol for methodology and claim boundaries.

Fixed-period momentum evidence

The same command also downloads the official value-weighted portfolios formed on NYSE prior (2-12) return decile breakpoints. It reports a P10-minus-P1 research spread and cross-decile monotonicity over fixed retrospective partitions:

Period Annualized mean spread Annualized volatility Newey-West t-stat Mean-return rank correlation
Development, 1927-1989 15.59% 26.51% 5.11 1.00
Validation, 1990-2014 11.60% 28.40% 1.89 0.93
Holdout, 2015-May 2026 8.72% 30.36% 1.05 0.37

The latest period is the important result: the average spread remains positive, but dependence-aware evidence and cross-decile monotonicity weaken materially. Only four of nine adjacent mean-return steps are positive in the holdout. This is reported as instability, not reframed as a successful trading strategy. The partitions are fixed for reporting but are not claimed to be historically untouched by the broader momentum literature.

What the Project Does

The repository implements an end-to-end research baseline:

  • deterministic generation of 100 synthetic stocks over 120 monthly observations;
  • validation of required columns, valid dates, unique (date, ticker) keys, positive prices, and consecutive monthly observations within each ticker;
  • 12-to-1 momentum, book-to-market value, ROE quality, and trailing low-volatility signals;
  • a conservative three-month demo lag for fundamental variables;
  • monthly cross-sectional 1st/99th percentile winsorization and z-score normalization;
  • an equal-weight composite requiring all four factor scores;
  • factor Rank IC, IC standard deviation, valid-observation positive-IC ratio, ICIR, and five-quantile return diagnostics;
  • a top-20% equal-weight long-only portfolio and equal-weight eligible-universe benchmark;
  • weight-based one-way turnover and configurable transaction costs;
  • 0/5/10/20/50 bps cost-sensitivity analysis;
  • portfolio return, volatility, zero-risk-free-rate Sharpe ratio, drawdown, hit-rate, turnover, and cost outputs;
  • official five-factor and momentum archive parsing, provenance hashes, fixed-subperiod analysis, and Newey-West mean inference;
  • twenty-one automated tests covering the synthetic pipeline, risk controls, and offline public-data parsing and statistics;
  • reproducible CSV results and an equity-curve chart written to results/.

Research Question

Can a transparent combination of momentum, value, quality, and low-volatility characteristics produce a stable cross-sectional ranking after explicit timing, turnover, benchmark, and transaction-cost controls?

The stock-level pipeline answers this question only inside a controlled synthetic experiment. The public module adds real constructed factor-portfolio evidence, but moving to a stock-selection conclusion still requires historical security data with real publication timestamps, delisting returns, and documented universe membership.

Factor Definitions

Factor Implementation at signal month t Direction
Momentum close[t-1] / close[t-12] - 1 Higher is better
Value book_to_market shifted by 3 monthly rows per ticker Higher is better
Quality roe shifted by 3 monthly rows per ticker Higher is better
Low volatility Negative trailing 12-month return volatility ending at t-1 Higher is better

The momentum signal skips the most recent month. The three-month fundamental lag is a transparent demo convention, not a substitute for actual filing publication timestamps. A real-data study must use the information that was genuinely available on each decision date.

Research Pipeline

  1. Generate the fixed-seed synthetic panel or load an external monthly CSV.
  2. Validate the data contract and sort each security chronologically.
  3. Build one-month future returns without crossing ticker boundaries.
  4. Construct the four lagged signals.
  5. Winsorize and standardize each signal within each month.
  6. Average all four z-scores into a composite; incomplete rows remain ineligible.
  7. Calculate monthly factor Rank IC against next-month returns and split each raw factor into five equal-count rank quantiles.
  8. Export each quantile's equal-weight next-month return and the top-minus-bottom spread.
  9. Select the top 20% of composite-score names and assign equal weights.
  10. Drift prior holdings by their realized returns, then calculate one-way turnover as 0.5 * sum(abs(target_weight - pretrade_weight)); initial investment turnover is 100%.
  11. Deduct turnover * cost_bps / 10,000 from portfolio return.
  12. Compare net portfolio return with the gross equal-weight return of the same eligible universe.
  13. Export portfolio, factor, quantile, cost-sensitivity, metric, and chart artifacts.

Rank IC is computed as the correlation between cross-sectional factor ranks and next-month return ranks. The positive-IC ratio uses only non-missing monthly IC observations. The reported ICIR is mean Rank IC divided by its sample standard deviation; it is not annualized. Quantile assignment ranks ties deterministically, applies qcut to create five groups, and defines the spread as Q5 minus Q1.

Repository Layout

quant-factor-research/
|-- README.md
|-- environment.yml
|-- pyproject.toml
|-- requirements.txt
|-- requirements-dev.txt
|-- run_research.py
|-- run_public_factor_validation.py
|-- data/
|   |-- README.md
|   `-- tushare_data_loader.py
|-- docs/
|   |-- factor_research_report.md
|   |-- failure_analysis.md
|   |-- public_factor_validation.md
|   |-- project_introduction.md
|   |-- resume_bullets.md
|   `-- logs/
|       |-- daily_report_template.md
|       `-- weekly_report_template.md
|-- examples/
|   `-- run_real_data_backtest.py
|-- results/
|   |-- cost_sensitivity.csv
|   |-- equity_curve.png
|   |-- factor_ic.csv
|   |-- factor_ic_summary.csv
|   |-- factor_quantile_returns.csv
|   |-- factor_quantile_summary.csv
|   |-- metrics.csv
|   |-- monthly_returns.csv
|   `-- public_factors/
|       `-- official_current_snapshot.csv
|-- src/
|   `-- quant_factor/
|       |-- __init__.py
|       |-- backtest.py
|       |-- data.py
|       |-- factors.py
|       |-- metrics.py
|       |-- public_data.py
|       `-- risk.py
`-- tests/
    |-- test_research.py
    |-- test_public_data.py
    `-- test_risk.py

Installation

From the repository root:

python -m venv .venv

Activate the environment, then install the pinned dependency ranges:

python -m pip install -r requirements.txt

Required packages are NumPy, pandas, Matplotlib, SciPy, and bottleneck. Python 3.10 or later is recommended.

Reproduce the Synthetic Baseline

Run the full pipeline from the repository root:

python run_research.py

Run the tests:

python -m pytest -q

Expected test summary:

21 passed

Change the transaction-cost assumption or synthetic seed:

python run_research.py --cost-bps 20 --seed 11

Write outputs to a separate experiment directory:

python run_research.py --output-dir results/seed_11 --seed 11

Run With an External Panel

python run_research.py --input data/public_monthly_panel.csv

The input CSV must contain:

Column Type Requirement
date date Monthly observation date; cannot be missing
ticker string Stable security identifier; cannot be missing
close float Positive when present; consistently adjusted
book_to_market float Fundamental value proxy
roe float Fundamental quality proxy

Each (date, ticker) pair must be unique, and each ticker's observations must advance by exactly one calendar month. A missing month raises an error because row-based lags would otherwise change the intended return horizon. The loader checks schema and basic integrity, but the user remains responsible for corporate-action adjustment, publication timing, delisting returns, survivorship, universe construction, and redistribution rights.

Do not commit employer data, paid vendor data, confidential code, client information, API keys, proprietary parameters, or internal screenshots.

Generated Results

python run_research.py writes:

File Contents
results/monthly_returns.csv Signal date, gross/net/benchmark/excess returns, turnover, cost, and universe counts
results/factor_ic.csv Monthly Rank IC for each raw factor
results/factor_ic_summary.csv Mean Rank IC, IC standard deviation, positive ratio, observation count, and ICIR
results/factor_quantile_returns.csv Monthly Q1-Q5 next-month returns and top-minus-bottom spread by factor
results/factor_quantile_summary.csv Time-series mean of each factor's quantile returns and spread
results/metrics.csv Net portfolio summary metrics and synthetic-data flag
results/cost_sensitivity.csv Performance at 0/5/10/20/50 bps
results/equity_curve.png Growth of one dollar for the portfolio and benchmark
results/run_manifest.json Machine-readable data provenance, factor specification, portfolio rule, and costs
results/public_factors/official_current_snapshot.csv Official May 2026 real factor-return snapshot with source URL
results/public_factors/latest/factor_summary.csv Full-history real factor metrics with Newey-West inference
results/public_factors/latest/momentum_decile_summary.csv Fixed-period momentum spread and monotonicity evidence
results/public_factors/latest/source_metadata.json Source URLs, archive members, SHA-256 hashes, and claim boundaries

Default Seed-7 Synthetic Check

The committed artifacts currently report the following 10 bps synthetic baseline:

Metric Synthetic result
Evaluated months 106
Annualized return 10.22%
Annualized volatility 4.54%
Sharpe ratio, zero risk-free rate 2.17
Maximum drawdown -3.67%
Positive-month ratio 74.53%
Average one-way turnover 25.63%

The synthetic generator explicitly loads returns on latent value and quality characteristics and includes short-term return persistence. These numbers are therefore expected pipeline behavior, not evidence that the factor combination works in a real market. Do not use them as performance claims in a resume, application essay, or interview.

The factor diagnostics reinforce that boundary: in the default simulation, mean Rank IC is approximately 0.0016 for momentum, 0.0248 for value, 0.0427 for quality, and 0.0111 for low volatility. They describe this specific simulation only.

Momentum remains in the registered four-factor baseline despite its near-zero synthetic IC. Removing an inconvenient pre-specified signal after seeing the result would create selection bias. A real-data stage should compare pre-declared 6-1, 9-1, and 12-1 variants on development data, select at most one on validation, and report the frozen variant once on the final test period.

Cost Sensitivity

The pipeline reruns the complete portfolio calculation at five cost assumptions. For the committed synthetic seed, annualized return declines from 10.55% at 0 bps to 8.88% at 50 bps. This is a mechanical sensitivity check, not a calibrated estimate of real commissions, spread, impact, taxes, or capacity.

Test Coverage

The twenty-one tests verify that:

  1. synthetic generation is deterministic for a fixed seed;
  2. future returns do not leak across ticker boundaries;
  3. momentum uses only the intended prior prices;
  4. initial one-way turnover equals full investment;
  5. annualized return matches a hand-checkable constant-return case;
  6. drawdown includes a negative first month;
  7. the composite remains unavailable until all four factor values exist;
  8. a missing future return among eligible names raises an error instead of being silently dropped;
  9. a missing monthly row within a ticker is rejected before row-based lags are calculated;
  10. an intermediate month with no future returns raises instead of being skipped;
  11. turnover compares target weights with return-drifted pretrade weights.
  12. an undersized eligible universe after the backtest starts raises instead of silently skipping a holding period.
  13. the public-data parser reads the monthly block, converts percentages to decimals, and stops before annual summaries;
  14. real factor summaries produce finite dependence-aware statistics over the declared period.
  15. the named value-weighted monthly portfolio block is selected without mixing equal-weighted or annual sections.
  16. fixed momentum reporting partitions and cross-decile monotonicity statistics are well formed.
  17. volatility targeting scales a raw weight toward the target volatility;
  18. volatility targeting respects the maximum single-name weight cap;
  19. volatility targeting returns zero for missing or non-positive realized volatility;
  20. the drawdown stop latches to zero after a drawdown breach;
  21. a corrupt cached public-data archive is deleted and redownloaded automatically.

The tests reduce implementation risk but do not validate the economic hypothesis or prove the absence of every form of leakage.

Known Limitations

  • Stock-level strategy results remain synthetic-only; the committed real-data artifact contains official aggregate factor portfolios, not security selections.
  • The simulation has no delistings, index changes, trading halts, corporate actions, liquidity limits, or market-capacity constraints.
  • A uniform three-month accounting lag is only a demo approximation to real publication timing.
  • Signals use information ending at t-1 or earlier, but the monthly model assumes rebalancing at the t close and does not simulate next-session prices, spread, or slippage.
  • The equal-weight benchmark is gross of its own rebalancing costs, while portfolio excess return uses the net strategy return.
  • No industry, size, beta, or country neutralization is applied.
  • There is no train/validation/test split because the current dataset is a pipeline simulation, not a fitted predictive model.
  • Costs are linear in turnover and omit spread, nonlinear impact, borrow, tax, and capacity effects.
  • Stock-level IC inference does not yet include Newey-West errors or multiple-testing correction; the public factor module adds Newey-West mean inference and fixed regime summaries only for aggregate factor portfolios.

Next Research Stage

The synthetic engine and public factor validation are complete for their stated scope. The next empirical stock-level stage is to:

  • source legally usable historical data with point-in-time fundamentals and delisting returns;
  • define an investable universe and explicit next-session execution rule;
  • add statistical inference, stability checks, and cost attribution for factor-quantile spreads, plus benchmark-cost diagnostics;
  • measure industry, size, beta, and liquidity exposures;
  • lock train, validation, and test periods before parameter selection;
  • test subperiod, regime, parameter, and capacity sensitivity;
  • document failed hypotheses alongside successful ones.

The ordered closure criteria and evidence required for each step are maintained in docs/failure_analysis.md. Items remain labeled open until code, data provenance, tests, and regenerated outputs support closure.

Maintainer

WenqiDing-CompFin

Disclaimer

This repository is for education and portfolio demonstration only. It is not financial advice, a solicitation, or a claim of live trading performance.


Real-World Data Integration Guide

The research engine consumes a standardized monthly panel through the data layer. The default pipeline calls generate_synthetic_panel(). To use real data, replace only the data layer and load a CSV that satisfies the documented schema.

Conceptual minimum fields required by the research layer:

Field Type Requirement
date datetime Monthly observation date
asset_id str Stable security identifier
return float One-month return for the asset
factor_value float Point-in-time factor input

The current quant_factor.data.validate_panel contract expects the columns date, ticker, close, book_to_market, and roe. A real-data adapter should therefore map vendor fields to this existing schema; the backtest and factor layers do not need to change.

Suggested data sources:

Source Market Access Notes
Tushare China A-shares Token required Prices, fundamentals, calendars; prefer point-in-time fields where available
Yahoo Finance US equities Public API Prices and basic fundamentals; publication timing and delisting handling must be verified

The data adapter should handle corporate actions, missing months, delisting returns, and publication timing before calling load_panel.

Minimal pseudocode:

def load_real_panel(source):
    # 1. fetch raw data from source
    # 2. clean and adjust for corporate actions
    # 3. map vendor fields to date, ticker, close, book_to_market, roe
    # 4. return a DataFrame that passes validate_panel

Tushare Pro A-share daily data is the first real data source; see the Real-World A-share Data Pipeline section.

Out-of-Sample Testing and Overfitting Discussion

The current framework already provides:

  • Deterministic chronological train/validation/test splits
  • Validation-based alpha selection
  • Fixed calendar partitions for public factor data
  • Held-out test evaluation opened once

Common risks in multi-factor research:

  • Factor decay: predictive power weakens after publication or implementation.
  • Overfitting: complex factor combinations fit noise rather than economic structure.
  • Data snooping: repeatedly testing many factors on the same history inflates apparent significance.
  • Regime dependence: factor spreads and monotonicity can vary across market states.
  • Survivorship and universe bias: excluding delisted or newly listed names distorts returns.

Current limitation: the primary stock-level pipeline still uses simulated data, so its out-of-sample statistics validate the research process, not a tradable edge.

Planned improvements:

  • Walk-forward and rolling-origin evaluation
  • Point-in-time fundamental data
  • Delisting return handling
  • Multiple-testing correction or deflated Sharpe
  • Factor stability and turnover diagnostics across subperiods

The planned protocol is walk-forward evaluation with an expanding training origin, one final held-out period, parameters frozen before the test period, Newey-West t-statistics, and multiple-testing controls.

Failed Experiments and Ablation Study Notes

This section is intentionally a research log, not a results showcase.

Experiment Observation Lesson
Technical factor screens such as volume/liquidity proxies Rank IC was not statistically significant in the tested sample Factors need an economic rationale plus multiple-testing awareness
Simple sum composite vs winsorized z-score composite Direct summation underperformed standardized combination Standardization and outlier treatment materially affect factor combination
Factor behavior in high-volatility regimes Some factors lost monotonicity or spread stability Regime dependence should be reported explicitly rather than averaged away

Detailed sample statistics will be added after a token-based Tushare run. The qualitative lessons above are reported without fabricated performance numbers.


Real-World A-share Data Pipeline

The repository now includes a data-layer adapter for real China A-share data through Tushare Pro. The adapter lives in data/tushare_data_loader.py and does not change the existing factor or backtest logic.

Dependencies

pip install tushare

Set the Tushare Token

export TUSHARE_TOKEN=your_token_here

Never hard-code the token in source code.

Run the Pipeline on Real A-share Data

python examples/run_real_data_backtest.py \
  --data real \
  --start-date 20230101 \
  --end-date 20231231 \
  --sample-size 100

The example computes factor IC, quantile returns, a long-only backtest, and an equity-curve chart using the existing pipeline functions. Results are written to:

results/real_data_ashare/

The simulated dataset remains fully functional:

python examples/run_real_data_backtest.py --data simulated

Limitations

  • Tushare Pro requires an account, token, and sufficient API points.
  • Delisted stocks are excluded by list_status="L", so survivorship bias remains a risk.
  • Suspended trading days are filtered using zero-volume observations where available.
  • The adapter currently covers the A-share market only.
  • sample_size limits the number of tickers to reduce API usage; results depend on the selected sample.
  • Data latency, corporate-action handling, and point-in-time fundamentals must be validated before any production use.

Research Disclaimer

Real-data experiments may be limited by API quota. Results are for academic research only and are not investment advice. A-share data may contain survivorship bias, and the current adapter does not reconstruct delisting returns or fully point-in-time fundamentals.

About

Reproducible multi-factor equity research with simulated panels, Fama-French evidence, and an experimental A-share Tushare data adapter.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages